http://www.elijahqi.win/archives/3529
有k次交换数字的机会 求从1,1->n,m能够获得的最大代价是多少
因为有很多路径代价是相同的但是选择的效果却不同 在月赛的时候我直接朴素dp+贪心 秒wa
思路非常巧妙 想hack icefox大爷没成功设dp[i][j][t][z]表示当前处在i,j 这个位置 现在一路走过来有t个位置没选 z个位置是从非路径上选来的 有z个 那么分两种情况讨论即可 1、直接向右走1步
2、向下走一步这两种情况都需要分别考虑我新走到的地方是否选择了 其他的k个即 从i.j+1->i,m
i+1,1->i+1,j-1这些位置中我可能是换过来的即 非路径上的点的权值
另外因为存在不合法状态 一开始需要给-inf
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
return x*f;
}
const int N=60;
int dp[N][N][22][22],T,a[N][N],n,m,k,q[N];//(i,j)位置 t个没选 z个另选的最优值
inline bool cmp(const int &a,const int &b){return a>b;}
int main(){
freopen("bzoj5359.in","r",stdin);
T=read();
while(T--){
n=read();m=read();k=read();memset(dp,128,sizeof(dp));
for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) a[i][j]=read();
dp[1][1][0][0]=a[1][1];dp[1][1][1][0]=0;
for (int i=1;i<=n;++i){
for (int j=1;j<=m;++j){
if (i!=n) {int nn=0;
for (int ii=j+1;ii<=m;++ii) q[++nn]=a[i][ii];
for (int ii=1;ii<j;++ii) q[++nn]=a[i+1][ii];sort(q+1,q+nn+1,cmp);
for (int ii=1;ii<=nn;++ii) q[ii]=q[ii-1]+q[ii];
for (int t=0;t<=k;++t){
for (int z=0;z<=k;++z){
for (int x=0;x<=nn&&x+z<=k;++x){
if (t<k) dp[i+1][j][t+1][z+x]=max(dp[i+1][j][t+1][z+x],dp[i][j][t][z]+q[x]);
dp[i+1][j][t][z+x]=max(dp[i+1][j][t][z+x],dp[i][j][t][z]+q[x]+a[i+1][j]);
}
}
}
}
if (j==m) continue;
for (int t=0;t<=k;++t){
for (int z=0;z<=k;++z){
dp[i][j+1][t][z]=max(dp[i][j+1][t][z],dp[i][j][t][z]+a[i][j+1]);
if (t<k) dp[i][j+1][t+1][z]=max(dp[i][j+1][t+1][z],dp[i][j][t][z]);
}
}
}
}int ans=0;
for (int i=0;i<=k;++i) ans=max(ans,dp[n][m][i][i]);printf("%d\n",ans);
}
return 0;
}