帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n × m n \times m n×m 的矩阵,矩阵中的每个元素 a i , j a_{i,j} ai,j 均为非负整数。游戏规则如下:
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入文件包括 n + 1 n+1 n+1 行:
第一行为两个用空格隔开的整数 n n n 和 m m m。
第 2 ∼ n + 1 2\sim n+1 2∼n+1 行为 n × m n \times m n×m 矩阵,其中每行有 m m m 个用单个空格隔开的非负整数。
输出文件仅包含 1 1 1 行,为一个整数,即输入矩阵取数后的最大得分。
2 3
1 2 3
3 4 2
82
【数据范围】
对于 60 % 60\% 60% 的数据,满足 1 ≤ n , m ≤ 30 1\le n,m\le 30 1≤n,m≤30,答案不超过 1 0 16 10^{16} 1016。
对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 80 1\le n,m\le 80 1≤n,m≤80, 0 ≤ a i , j ≤ 1000 0\le a_{i,j}\le1000 0≤ai,j≤1000。
【题目来源】
NOIP 2007 提高第三题。
本题用区间dp解决
考虑到每行数字互不相干,可以把每行依次解决后相加
每行用区间dp,由于本题特殊性,可以从大区间推到小区间
在区间 [ i , j ] [i,j] [i,j]时,从区间 [ i − 1 , j ] [i-1,j] [i−1,j] 和 [ i , j + 1 ] [i,j+1] [i,j+1] 来
此时选中的数字是第 m + i − j − 1 m+i-j-1 m+i−j−1 个(自行推导)
于是状态转移方程便是
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] + 2 m + i − j + 1 ∗ a [ i − 1 ] , d p [ i ] [ j + 1 ] + 2 m + i − j + 1 ∗ a [ j + 1 ] ) dp[i][j]=max(dp[i-1][j]+2^{m+i-j+1}*a[i-1],dp[i][j+1]+2^{m+i-j+1}*a[j+1]) dp[i][j]=max(dp[i−1][j]+2m+i−j+1∗a[i−1],dp[i][j+1]+2m+i−j+1∗a[j+1])
注意本题需用高精
我用__int128水过了
还有pow的精度问题,不要用pow,自己预处理2的几次方
#include
#include
#include
using namespace std;
int n,m;
__int128 a[100];
__int128 dp[100][100],ans2,base[200];
inline __int128 read(){
__int128 x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void print(__int128 x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
void init() {
base[0] = 1;
for (int i = 1; i <= m + 2; i++)
base[i] = base[i - 1] * 2;
}
int main()
{
cin>>n>>m;
init();
while(n--){
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++)
a[i]=read();
for(int i=1;i<=m;i++)
for(int j=m;j>=1;j--){
dp[i][j]=max(dp[i][j],dp[i-1][j]+base[m-j+i-1]*a[i-1]);
dp[i][j]=max(dp[i][j],dp[i][j+1]+base[m-j+i-1]*a[j+1]);
}
__int128 ans=0;
for(int i=1;i<=m;i++)
ans=max(ans,dp[i][i]+base[m]*a[i]);
ans2+=ans;
}
print(ans2);
return 0;
}