noip提高组2007 矩阵取数游戏(luogu p1005)
原题链接:https://www.luogu.org/problem/show?pid=1005
DP+高精。
首先可以证明贪心是错误的,很容易就能找到反例。
于是考虑DP,对于每一行,首先预处理每个数最后选,然后枚举区间长度,进行区间DP:
f[i][j]=max(f[i][j+1]+a[i]*2^(m-i-j),f[i+1][j]+a[i]*2^(m-i-j));
f[i][m]即为每一行能取到的最大值,加入答案即可。
m<=80,仅仅是2^m就足够炸掉longlong,而luogu上一些人用了_int128这种黑科技。。。
不是很喜欢用这种不稳定的东西,于是就用结构体写了个朴素的高精,毕竟能够直接传递一个结构体真是太赞了。
起初只有30分,然后把高精的压位变成4位之后,就A掉了,不得不说这真是压位高精的大胜利。
#include<cstdio> #include<cstring> struct num { int c[105],w; }f[105][105],ans; num s[105]; int a[105][105],n,m; int max(int x,int y) { return x>y?x:y; } void print(num x) { printf("%d",x.c[x.w]); for(int i=x.w-1;i>0;i--) { if(x.c[i]<1000) printf("0"); if(x.c[i]<100) printf("0"); if(x.c[i]<10) printf("0"); printf("%d",x.c[i]); } } num mul(num x,int y) { num t; t.w=x.w;t.c[t.w+1]=0; for(int i=1;i<=x.w;i++) t.c[i]=x.c[i]*y; for(int i=1;i<=x.w;i++) { if(t.c[i]>=10000) { t.c[i+1]+=t.c[i]/10000; t.c[i]%=10000; } } if(t.c[t.w+1]!=0) t.w++; return t; } num add(num x,num y) { num t; int l=max(x.w,y.w);t.w=l;t.c[l+1]=0; for(int i=1;i<=l;i++) t.c[i]=x.c[i]+y.c[i]; for(int i=1;i<=l;i++) { if(t.c[i]>=10000) { t.c[i+1]+=t.c[i]/10000; t.c[i]%=10000; } } if(t.c[t.w+1]!=0) t.w++; return t; } num getmax(num x,num y) { if(x.w>y.w) return x; if(x.w<y.w) return y; for(int i=x.w;i>=1;i--) { if(x.c[i]>y.c[i]) return x; if(x.c[i]<y.c[i]) return y; } return x; } int main() { // freopen("testdata.in","r",stdin); scanf("%d %d",&n,&m); s[0].c[1]=1;s[0].w=1; for(int i=1;i<=m;i++) s[i]=mul(s[i-1],2); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); } for(int k=1;k<=n;k++) { for(int i=1;i<=m;i++) f[i][i]=mul(s[m],a[k][i]); for(int l=1;l<m;l++) { for(int i=1;i+l<=m;i++) { int j=i+l; num ans1=add(f[i][j-1],mul(s[m+i-j],a[k][j])); num ans2=add(f[i+1][j],mul(s[m+i-j],a[k][i])); f[i][j]=getmax(ans1,ans2); } } ans=add(ans,f[1][m]); // print(ans); // printf("\n"); } print(ans); return 0; }