题目大意:
- 有n*m的一个矩阵,矩阵中每个元素都为非负整数。
- 取m次数,每次取n个,只能从该行的首和尾取,取得的分数为这个数 * 2的i次(i表示第i次取数,从1标号)
- 求最大的分数。
题解思路
- 区间dp不说话,每次从头或尾取,得出动态转移方程dp[i][j]=max(dp[i+1][j]+a[k][i]*2^次数,dp[i][j-1]+a[k][j]*2^次数) (k是行数)。
- 答案就是每行dp[1][n]的和。
- 因为n, m<=80,所以2^m会爆long long,而高精度又有点慢(打起来太麻烦),就可以使用128位整数__int128。
- 输出需要手打
细节
- 记得手打输出要判零
上代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define rep(x, l, r) for (int x = l; x <= r; x++)
#define repd(x, r, l) for (int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define pb push_back(x)
#define MAXN 85
#define INF 1 << 30
typedef __int128 BLL;
using namespace std;
BLL a[MAXN][MAXN], dp[MAXN][MAXN];
void write(BLL x, bool flag)
{
if (!x && flag){
return;
}
if (x)
write(x / 10, 1);
putchar(x % 10 + '0');
}
void print(BLL x){
write(x, 0);
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
rep(i, 1, n)
{
rep(j, 1, m)
{
int x;
scanf("%d", &x);
a[i][j] = x;
}
}
BLL ans = 0;
rep(k, 1, n)
{
clr(dp, 0);
rep(l, 1, m)
{
rep(i, 1, m - l + 1)
{
int j = i + l - 1;
dp[i][j] = max(dp[i + 1][j] + (a[k][i] << (m - l + 1)), dp[i][j - 1] + (a[k][j] << (m - l + 1)));
}
}
ans += dp[1][m];
}
print(ans);
puts("");
return 0;
}