解题思路:
1、纯DP思路:
(1)、首先来定义一下dp数组:dp[x][y][num][maxv]表示走到(x,y)的位置时,选出了num件物品,最大价值的物品价值为maxv的行动方案数。
(2)、寻找状态转移方程:由题意可知,当小明到达某个状态时,因为小明只能只能向下或向右行走,所以当前位置(x,y)一定从从(x-1,y)和(x,y-1)两种情况转移过来的。
我们又已知当当前位置物品的价值比拿到的所有物品中价值最大的都要大时小明可以选择拿起它或者不拿,设当前位置物品的价值为cur。
如果小明要拿当前物品的话:dp[i] [j] [k] [cur]+=dp[i−1][j][k−1][0... cur−1]+dp[i][j−1][k−1][0... cur−1]
//拿走物品
for(int c=0;c<cur;c++)
dp[i][j][l][cur]=(dp[i][j][l][cur]+dp[i-1][j][l-1][c]+dp[i][j-1][l-1][c])%mod;
如果不拿当前物品的话:dp[i][j][k][c]+=dp[i−1][j][k][c]+dp[i][j−1][k][c],(c = 0, 1, 2 … 12)
//不拿走物品
for(int c=0;c<=12;c++)
dp[i][j][l][c]=(dp[i][j][l][c]+dp[i-1][j][l][c]+dp[i][j-1][l][c])%mod;
(3)、dp数组初始化:设(i,j)位置的物品的价值是g[i][j],那么dp[i][j][1][g[i][j]]表示从(1,1)走到当前位置只选当前位置上的物品的方案数,实际上她就等于从(1,1)走到当前位置的方案数,初始dp[1][1][1][g[1][1]]=1,然后进行如下操作:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!(i==1&&j==1))
dp[i][j][1][a[i][j]]=(dp[i][j][1][a[i][j]]+dp[i-1][j][1][a[i-1][j]]+dp[i][j-1][1][a[i][j-1]])%mod;
完整代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
long long dp[110][110][13][13];
int n,m,k;
int a[110][110];
int mod=1000000007;
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
dp[1][1][1][a[1][1]]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!(i==1&&j==1))
dp[i][j][1][a[i][j]]=(dp[i][j][1][a[i][j]]+dp[i-1][j][1][a[i-1][j]]+dp[i][j-1][1][a[i][j-1]])%mod;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int l=1;l<=k;l++)
{
int cur=a[i][j];
//拿走物品
for(int c=0;c<cur;c++)
dp[i][j][l][cur]=(dp[i][j][l][cur]+dp[i-1][j][l-1][c]+dp[i][j-1][l-1][c])%mod;
//不拿走物品
for(int c=0;c<=12;c++)
dp[i][j][l][c]=(dp[i][j][l][c]+dp[i-1][j][l][c]+dp[i][j-1][l][c])%mod;
}
int ans=0;
for(int i=0;i<=12;i++)
ans=(ans+dp[n][m][k][i])%mod;
cout<<ans<<endl;
return 0;
}
2、记忆化搜索+dp做法。当我们看到这个题一般很容易就能想到用dfs来做,但是只是单纯地用dfs会超时,所以我们要想办法去优化它,这时就可以用记忆化的方法(因为一旦到达当前点的状态确定了,那么从当前点到终点的方案数就确定了),而需要记录的状态和上面dp方法中的数组记录的状态是一样的,这种方法思路比较清晰明了就不做详细的介绍了,代码中有具体的注释。
直接上代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>
#include <stack>
using namespace std;
const int N=55;
int g[N][N];
long long dp[N][N][N][N];
int n,m,k;
int mod= 1000000007;
int dx[]={0,1},dy[]={1,0};
long long dfs(int x,int y,int num,int maxv)
{
//因为我们的初值设置为了-1,但是数组的下标不能为负数,所以统一加1
if(dp[x][y][num][maxv+1]!=-1) //如果说从当前点以现在的状态到终点的方案数在之前已经算出就直接返回
return dp[x][y][num][maxv+1];
long long ans=0;
if(x==n&&y==m)//如果说现在到了终点了,小明成功获得k件物品的情况无非有以下两种,一种是小明已经选够了k件物品
//还有一种可能是他已经选了k-1件物品,并且终点位置上物品恰好比当前选的所有物品的中任意一个价值都要高,那依旧可以成功
{
if(num==k||(num==k-1&&g[x][y]>maxv))
{
ans=(ans+1)%mod;
}
return ans;
}
for(int i=0;i<2;i++)
{
int tx=x+dx[i],ty=y+dy[i];
if(tx<1||tx>n||ty<1||ty>m)
continue;
if(g[x][y]>maxv&&num+1<=k)//如果说当前还没有选够k件物品,并且当前物品的价值比已经选了的任意一个物品的价值都要高,那小明就可以拿走这个物品
ans+=dfs(tx,ty,num+1,g[x][y]);
//无论当前物品能不能被拿走,小明都可以不拿它
ans+=dfs(tx,ty,num,maxv);
}
dp[x][y][num][maxv+1]=ans%mod;//将得到的答案记录下来
return dp[x][y][num][maxv+1];
}
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
memset(dp,-1,sizeof dp);
cout<<dfs(1,1,0,-1)<<endl;//物品价值最大值的初值要设置成-1,因为物品的价值最小可以是0
return 0;
}
搞了一个多晚上终于弄明白了,明天就是蓝桥杯省赛了,加油!