小象和老鼠 DP

比较有意思的一道DP题,还是比较简单。我们发现如果直接设\(f[i][j]\)跑会导致一些格子重复计算,所以我们可以设\(f[i][j][0]\)表示到位置\((i,j)\)时最少看到的老鼠数量,并且当前状态是从上面转移而来的\(f[i][j][1]\)表示到位置\((i,j)\)时最少看到的老鼠数量,并且当前状态是从左面转移而来的,这样我们便可以获得决策所需要的全部条件,从而避免重复计算。

转移方程看着图写就好了

//f[i][j][0]当前状态从上面转移而来
f[i][j][0]=min(f[i-1][j][1]+mp[i][j+1]+mp[i+1][j], f[i][j][0]);
f[i][j][0]=min(f[i-1][j][0]+mp[i+1][j]+mp[i][j-1]+mp[i][j+1], f[i][j][0]);
//f[i][j][0]当前状态从左面转移而来
f[i][j][1]=min(f[i][j-1][1]+mp[i][j+1]+mp[i-1][j]+mp[i+1][j], f[i][j][1]);
f[i][j][1]=min(f[i][j-1][0]+mp[i][j+1]+mp[i+1][j], f[i][j][1]);

完整代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 1010
using namespace std;
int f[MAXN][MAXN][2],mp[MAXN][MAXN];
int n,m;
int calc(int x, int y){
    int res=0;
    if(x+1>=1&&x+1<=n) res+=mp[x+1][y];
    if(x-1>=1&&x-1<=n) res+=mp[x-1][y];
    if(y+1>=1&&y+1<=m) res+=mp[x][y+1];
    if(y-1>=1&&y-1<=m) res+=mp[x][y-1];
    return res+mp[x][y];
}
int main(){
    scanf("%d %d", &n, &m);
    for(int i=0;i<=n;++i)
    for(int j=0;j<=m;++j)
        f[i][j][0]=f[i][j][1]=0x3f3f3f3f;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
        scanf("%d", &mp[i][j]);
    f[1][1][0]=f[1][1][1]=calc(1, 1);
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j){
        //f[i][j][0]当前状态从上面转移而来
        f[i][j][0]=min(f[i-1][j][1]+mp[i][j+1]+mp[i+1][j], f[i][j][0]);
        f[i][j][0]=min(f[i-1][j][0]+mp[i+1][j]+mp[i][j-1]+mp[i][j+1], f[i][j][0]);
        //f[i][j][0]当前状态从左面转移而来
        f[i][j][1]=min(f[i][j-1][1]+mp[i][j+1]+mp[i-1][j]+mp[i+1][j], f[i][j][1]);
        f[i][j][1]=min(f[i][j-1][0]+mp[i][j+1]+mp[i+1][j], f[i][j][1]);
    }
    printf("%d", min(f[n][m][0], f[n][m][1]));
    return 0;
}

这是校内模拟赛做的一道题,一开始以为是道DP签到题导致思路都错了,后面静下心慢慢分析决策才想出正解,可见手推样例重要性。另外一定不要轻敌。

01-22 07:06