HDU - 6289 寻宝游戏 详解(DP)

——————

寻宝游戏

小Q最近迷上了一款寻宝游戏,这款游戏中每局都会生成一个n×m的网格地图,从上往下依次编号为第1行到第n行,从左往右依次编号为第1列到第m列。每个格子上都有不同数量的金币,第i行第j列的格子上的金币数量为ai,j。

小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),依次表示每个格子中金币的数量。
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
——————

首先考虑如何暴力求出最优解决方案,一种很简单的做法就是枚举每一种走法,然后用改走法路径外的数值从大到小替换路径内从小到达的数值。——必TLE
参考了别人的在做法,使用动态规划解决这个问题

状态dp[x][y][used][blank]表示当前正在坐标(x,y),使用了used次交换将路径外的数值交换到路径内,但是目前只有blank个数被交换出去。显而易见used==blank的状
态才是最终情况下的正确状况。

dp的做法本质上还是在一定的走法路径上将路径内的数值和路径外的数值进行替换,我们需要知道如何能在状态转移中执行替换的操作,并且一个格子不会被替换到路径内多次,且所有路径外的格子都要被考虑到。

我的具体转移方法就是,当从坐标(x-1,y)转移到(x,y)时,考虑[x-1][y+1 to m]这段区间数值和[x][1 to y-1]这段区间的数值替换到路径内,向右走的时候不考虑替换操作,这样就能保证每个格子都被考虑到且不会被多次考虑到了。

具体转移方式见代码。

#include 
using namespace std;
typedef long long ll;
const int maxn=55;
const int maxk=25;
const int maxm=100005;
const int maxe=2*1e5+115;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int n,m,k;
int dp[maxn][maxn][maxk][maxk];
int ma[maxn][maxn];
int vec[maxn];
int tot;
inline bool cmp(int a,int b){
    return a>b;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(dp,-0x3f,sizeof(dp));
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0;ifor(int j=0;j<m;j++){
                scanf("%d",&ma[i][j]);
            }
        }
        dp[0][0][0][0]=ma[0][0];
        dp[0][0][0][1]=0;
        for(int x=0;xx++){
            for(int y=0;y<m;y++){
                if (x==0&&y==0)continue;
                tot=0;
                if(x)for(int jj=y+1;jj<m;jj++)vec[tot++]=ma[x-1][jj];
                for(int jj=0;jj<y;jj++)vec[tot++]=ma[x][jj];
                sort(vec,vec+tot,cmp);
                for(int used=0;used<=k;used++){
                    for(int blank=0;blank<=x+y+1&&blank<=k;blank++){
                        int ans=-inf;
                        if(x){
                            ans=max(ans,dp[x-1][y][used][blank]+ma[x][y]);
                            if(blank)ans=max(ans,dp[x-1][y][used][blank-1]);
                            int sum=0;
                            int it=0;
                            for(int cntuse=1;cntuse<=used&&cntuse<=tot;cntuse++){
                                sum+=vec[it++];
                                ans=max(ans,dp[x-1][y][used-cntuse][blank]+ma[x][y]+sum);
                                if(blank)ans=max(ans,dp[x-1][y][used-cntuse][blank-1]+sum);
                            }
                        }
                        if(y){
                            ans=max(ans,dp[x][y-1][used][blank]+ma[x][y]);
                            if(blank)ans=max(ans,dp[x][y-1][used][blank-1]);
                        }
                        dp[x][y][used][blank]=ans;
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<=k;i++){
            ans=max(ans,dp[n-1][m-1][i][i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(dp)