寻宝游戏
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 512000/512000 K (Java/Others)Total Submission(s): 165 Accepted Submission(s): 34
Problem Description
小Q最近迷上了一款寻宝游戏,这款游戏中每局都会生成一个
n×m的网格地图,从上往下依次编号为第
1行到第
n行,从左往右依次编号为第
1列到第
m列。每个格子上都有不同数量的金币,第
i行第
j列的格子上的金币数量为
ai,j。
小Q一开始位于 (1,1),每次他可以往右或者往下走,每当他经过某个格子时,他就可以拿走这个格子上的所有金币。小Q不能走出这个地图,当小Q不能再行动时,游戏结束。显然当且仅当小Q位于 (n,m)时,游戏才会结束。
一轮游戏的得分为这一轮中收集到的金币总量,而在游戏开始前,因为小Q是超级VIP用户,所以他有 k次机会交换某两个格子中的金币数。这 k次机会不一定要用完,请写一个程序帮助小Q在一轮内拿到尽可能多的金币。
小Q一开始位于 (1,1),每次他可以往右或者往下走,每当他经过某个格子时,他就可以拿走这个格子上的所有金币。小Q不能走出这个地图,当小Q不能再行动时,游戏结束。显然当且仅当小Q位于 (n,m)时,游戏才会结束。
一轮游戏的得分为这一轮中收集到的金币总量,而在游戏开始前,因为小Q是超级VIP用户,所以他有 k次机会交换某两个格子中的金币数。这 k次机会不一定要用完,请写一个程序帮助小Q在一轮内拿到尽可能多的金币。
Input
第一行包含一个正整数
T(1≤T≤10),表示测试数据的组数。
每组数据第一行包含三个整数 n,m,k(2≤n,m≤50,0≤k≤20),分别表示地图的长宽以及交换的次数。
接下来 n行,每行 m个整数 ai,j(0≤ai,j≤106),依次表示每个格子中金币的数量。
每组数据第一行包含三个整数 n,m,k(2≤n,m≤50,0≤k≤20),分别表示地图的长宽以及交换的次数。
接下来 n行,每行 m个整数 ai,j(0≤ai,j≤106),依次表示每个格子中金币的数量。
Output
对于每组数据,输出一行一个整数,即能收集到的金币数量的最大可能值。
Sample Input
2 3 4 0 1 2 3 4 9 8 7 6 5 4 7 2 5 5 1 9 9 9 0 0 0 0 9 0 0 0 0 0 0 0 0 0 9 0 0 9 0 9 9 9
Sample Output
34 81
Source
上为qls题解截图。
下边是我做题中的愚见:
题目一看就是DP,有一种从1,1到n,m的n^2的DP,然而这个多了k次交换,所以题目从2维DP变成了4维DP。
dp[i][j][p][q]:表示走到(i,j)位置必经之路上没拿了p个非必经之路上拿了q个。
状态转移方程:
dp[i][j][p][q] = max ( dp[i][j][p][q] , dp[i-1][j][p][q]+mp[i][j] );//从上边直接下来
dp[i][j][p][q] = max ( dp[i][j][p][q] , dp[i-1][j][p-1][q] );
dp[i][j][p][q] = max ( dp[i][j][p][q] , dp[i][j][p][q-r]+que[r] );
dp[i][j][p][q] = max ( dp[i][j][p][q] , dp[i][j-1][p][q]+mp[i][j] );
dp[i][j][p][q] = max ( dp[i][j][p][q] , dp[i][j-1][p-1][q] );
#include <bits/stdc++.h>
using namespace std;
int Map[55][55];
int dp[55][55][55][55];
int a[55];
int Index;
int cmp(int a,int b)
{
return a>b;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&Map[i][j]);
memset(dp,-1,sizeof(dp));
dp[1][1][0][0]=Map[1][1];//取了第一个
dp[1][1][1][0]=0; //没取
for(int i=1;i<=n;i++)//n*n*k*k*k
{
for(int j=1;j<=m;j++)
{
a[0]=0;
Index=0;
for(int k=1;k<=m;k++)//是从当前点往前和上一行当前点往后
{
if(k<j)
a[++Index]=Map[i][k];
else if(k>j)
a[++Index]=Map[i-1][k];
}
sort(a+1,a+Index+1,cmp); //从大到小排序 将那些不是必经之路上的点取出来
for(int k=1;k<=Index;k++)//求个前缀和
a[k]=a[k]+a[k-1];
for(int p=0;p<=k;p++)
{
for(int q=0;q<=k;q++)
{
if(dp[i-1][j][p][q]!=-1) //从上边过来(经过的路上)
dp[i][j][p][q]=max(dp[i][j][p][q],dp[i-1][j][p][q]+Map[i][j]);
if(p>=1 && dp[i-1][j][p-1][q]!=-1)//考虑有个点取还是没取 比如前边的那个第一个点
dp[i][j][p][q]=max(dp[i][j][p][q],dp[i-1][j][p-1][q]);
}
}
for(int p=k;p>=1;p--)
{
for(int q=k;q>=0;q--)
{
for(int r=p;r>=1;r--)//有多少非必经之路的
{
if(dp[i][j][q][p-r]!=-1 && p-r>=0)//取出来r个
dp[i][j][q][p]=max(dp[i][j][q][p],dp[i][j][q][p-r]+a[r]);
}
}
}
for(int p=0;p<=k;p++)
{
for(int q=0;q<=k;q++)
{
if(dp[i][j-1][p][q]!=-1)
dp[i][j][p][q]=max(dp[i][j][p][q],dp[i][j-1][p][q]+Map[i][j]);
if(p>=1 && dp[i][j-1][p-1][q]!=-1)
dp[i][j][p][q]=max(dp[i][j][p][q],dp[i][j-1][p-1][q]);
}
}
}
}
int ans=0;
for(int i=0;i<=k;i++)
ans=max(ans,dp[n][m][i][i]);
printf("%d\n",ans);
}
return 0;
}