HDU 1978 How many ways (DP)

一个比较简单的DP,希望读者在看题解之前再自己想想,争取自己AC 。

这是一个计数问题,问你从(1,1)点到(n,m)点的行走方案数,因为还有一个能量问题,一开始我为了将状态表示完整,试着开三维数组记忆化搜索,用d[i][j][k]表示当前在i,j点还有k个能量的方法数,但是总得不到正确的答案,后来我发现这么做是不正确的,为什么呢?我们要明确一点,动态规划的特点是具有很多重叠子问题,大的最优解依赖于局部最优解,且每一个状态都具有相似的意义 。 请读者注意最后一句话,这很重要 。 我们先来看题目的要求,求起点到n,m点的方案数,那么也就是说我们在n,m点一定停了,具有该点的能量,而我之前的那个表示方法却会出现很多这样的情况:在某个点却不拥有该点的能量 。简单的说,我们应该有一个具有相同意义的状态表示,用d[i][j]表示从起点到i,j点的方案数,是不是和要求的最终问题的表示方法是一样的?  

明白了这点,状态转移就很好写了,对于每一个状态,枚举所有可能到达的点,将状态转移过去。

推荐下一道DP题目:点击打开链接

细节参见代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<list>
#include<cmath>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int INF = 100000000;
const int mod = 10000;
const long long maxn = 110;
int T,n,m,d[maxn][maxn],a[maxn][maxn];
int dp(int i,int j) {
    if(i==n&&j==m) return 1;
    int& ans = d[i][j];
    if(ans != -1) return ans;
    ans = 0;
    for(int k=i;k<=n;k++) {
        for(int l=j;l<=m;l++) {
            if(k==i&&l==j) continue;
            if(k-i+l-j <= a[i][j]) ans = (ans + dp(k,l))%mod;
            else break;
        }
    }
    return ans;
}
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
        memset(d,-1,sizeof(d));
        int ans = dp(1,1);
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(dp,动态规划,ACM-ICPC)