P1004 方格取数
大意:找出从矩阵左上到右下获得分数最大的两条路径,求其最大分数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<vector>
using namespace std;
int n,x,y,z,a[20][20];
int f[15][15][15][15];
int main()
{
scanf("%d",&n);
memset(a,0,sizeof(a));
while(1)
{
scanf("%d%d%d",&x,&y,&z);
if(!x && !y && !z) break;
a[x][y] = z;
}
memset(f,0,sizeof(f));
for(int i = 1;i <= n; ++i)
for(int j = 1;j <= n; ++j)
for(int k = 1;k <= n; ++k)
for(int l = 1;l <= n; ++l)
{
f[i][j][k][l] = max(f[i - 1][j][k - 1][l],max(f[i - 1][j][k][l - 1],
max(f[i][j - 1][k - 1][l],f[i][j - 1][k][l - 1])));
f[i][j][k][l] += a[i][j] + a[k][l];
if(i == k && j == l) f[i][j][k][l] -= a[i][j]; //排除掉两个点重合的情况
}
printf("%d\n",f[n][n][n][n]);
return 0;
}
P1005 矩阵取数游戏 //区间DP
题目描述:
对于一个给定的n×m的矩阵,矩阵中的每个元素a{i,j}均为非负整数。游戏规则如下:
(1)每次取数时须从每行各取走一个元素,共nn个。经过mm次后取完矩阵内所有元素;
(2)每次取走的各个元素只能是该元素所在行的行首或行尾;
每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 *2^i,其中i表示第i次取数(从1开始编号);
(3)游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分
分析:
高精度太难写了(写个60分的吧)
f[i][j] = max(f[i - 1][j] + a[i - 1] * 2 ^(m - j + i - 1), f[i][j + 1] + a[j + 1] * 2 ^ (m - j + i - 1));
注意这种方式区间是从大到小的,所以结束条件是取这一行 f[i][j] + a[i] * 2 ^ m 的最大值
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<vector>
using namespace std;
long long f[100][100];
long long a[100][100];
int n,m,k;
long long ans;
long long maxi(long long x,long long y)
{
if(x > y) return x;
return y;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n; ++i)
for(int j = 1;j <= m; ++j)
scanf("%d",&a[i][j]);
memset(f,0,sizeof(f));
ans = 0;
for(k = 1;k <= n; ++k)
{
for(int i = 1;i <= m; ++i)
for(int j = m; j >= i; --j)
{
f[i][j] = maxi(f[i - 1][j] + pow(2 , m - j + i - 1) * a[k][i - 1],
f[i][j + 1] + pow(2 , m - j + i - 1) * a[k][j + 1]);
}
long long t = 0;
for(int i = 1;i <= m; ++i)
t = maxi(f[i][i] + a[k][i] * pow(2,m),t);
ans += t;
}
printf("%lld\n",ans);
return 0;
}
还有另一种方式f[i][len],表示从i开始,长度为len的区间max值
int n, m;
__int128 game[MAXN][MAXN];
__int128 f[MAXN][MAXN];
__int128 solve(__int128 a[])
{
memset(f,0,sizeof(f));
for(int len=0;len<=m;++len)
for(int i=1;i+len<=m;++i)
f[i][i+len]=max(2*f[i+1][i+len]+2*a[i],2*f[i][i+len-1]+2*a[i+len]);
return f[1][m];
}
P1018 乘积最大
题目大意:
有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
分析:
还是高精度,不写hhh 60分
f[i][j]表示在前i个位置插入了j个乘号所得到的最大乘积
枚举上一个乘号出现的位置
f[i][j] = max(f[l][j - 1] * s[l + 1][i]) 0 <= l < i
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<vector>
using namespace std;
long long f[50][50];
string a;
long long s[50][50];
int n,k;
int main()
{
scanf("%d%d",&n,&k);
cin>>a;
f[0][0] = (int)(a[0] - '0');
for(int i = 0;i < n; ++i)
for(int j = i; j < n; ++j)
s[i][j] = s[i][j - 1] * 10 + (int)(a[j] - '0');
for(int i = 1;i < n; ++i)
for(int j = 0;j <= k; ++j)
{
if(j == 0) f[i][j] = f[i - 1][j] * 10 + (int)(a[i] - '0');
else
{
for(int l = 0;l < i; ++l)
f[i][j] = max(f[i][j],f[l][j - 1] * s[l + 1][i]);
}
}
printf("%lld\n",f[n - 1][k]);
return 0;
}