一开始想的就是报暴搜,结果T了
然后网上寻得大佬的代码,开一个四维DP,就很灵性。
解题思路:
设d[i][j][k][c]为走到(i,j)的时候,手上共K个物品,最大价值小于c, 则d[i][j][k][c]转移方程是:
当前拿这个物品: s1= d[i-1][j][k-1][ w[i][j] ]+d[i][j-1][k-1][ w[i][j] ]
当前可以拿这个物品的条件是(c>a[i][j]&&k>0);
当前不拿物品 : s2= d[i-1][j][k][c]+d[i][j-1][k][c]
综合 d[i][j][k][c]= (s1+s2)%mod;
最大时间复杂度O(T)=O(ijkc)=O(50501313)=O(422500)
注意事项:
初始化(i == 0||j == 0)的时候d[i][j][k][c]=0;
临界条件( i== 1 && j == 1)当k!=0或者 k==1且c>a[i][j]的时候设置为1,其他时候0;
答案是d[n][m][k][12];
#include <iostream>
#define _for(i,a,b) for(int i=a;i<b;i++)
using namespace std;
typedef long long LL;
LL d[51][51][13][13],mod=1000000007;
int a[55][55];
int main() {
int n,m,K;
cin>>n>>m>>K;
_for(i,0,n)_for(j,0,m)cin>>a[i+1][j+1];
_for(i,1,n+1)_for(j,1,m+1)_for(k,0,K+1)_for(c,0,13){
LL na=0,buna=0;
if(i==1&&j==1){
if(!k||(k==1&&c>a[i][j]))d[i][j][k][c]=1;
continue;
}
if(k&&c>a[i][j]) na=d[i-1][j][k-1][a[i][j]]+d[i][j-1][k-1][a[i][j]];
buna=d[i-1][j][k][c]+d[i][j-1][k][c];
d[i][j][k][c]=na+buna;
d[i][j][k][c]%=mod;
}
cout<<d[n][m][K][12]<<endl;
}
然后暴搜+记忆化搜索也能AC
#include<iostream>
#include<cstring>
using namespace std;
/* dp[x][y][num][v+1]和dfs(int x,int y,int num,int v) dp数组用于存储防止重复计算
不用 dp[x][y][num][v]存储应为v必须初始化-1,
如果初始化0,当宝物价值为0时,map[x][y]==v,无法拾取宝物,
当为v=-1数组无法存,只能向后错一位存;
建模:
dfs(int x,int y,int num,int v)
点(x,y)到点(n,m)还要再取num个宝物且宝物必须大于v的取法种数;
边界:当x==n,y==m;已经到最后一个宝物;
1.num==0不需要再去宝物 返回1;
2.num==1&&map[n][m]>v 还要再取一个且能取 返回1
用数组记录并返回dp[x][y][num][v+1]=1;
递归方程:
dfs(x,y,num,v) =
当v<=map[x][y] 该宝物能取
dfs(x+1,y,num+1,map[x][y])+dfs(x+1,y,num,v)+dfs(x,y+1,num+1,map[x][y])+dfs(x,y+1,num,v);
当v>map[x][y] 该宝物不能取
dfs(x+1,y,num,v)+dfs(x,y+1,num,v);
总结:根据题目条件求(1,1)到(n,m)取k件宝物的总走法
只能往右下走所以肯定能从(1,2)到(n,m) (2,1)到(n,m)取k-1件or k件宝物的走法中获得;
因为不是随便取,必须取大于先前取得的宝物价值 所以要一个变量v记录所以4个变量
则建立dfs(x,y,num,v)
dfs(1,1,k,-1)=........
*/
const int N=1000000007;
int n,m,k;
int map[51][51];
int dp[51][51][13][14];
int dfs(int x,int y,int num,int v){//num代表还要取num个宝物
if(dp[x][y][num][v+1]!=-1)return dp[x][y][num][v+1];//能在记忆数组中找到
int t=0;
if(x==n&&y==m){//边界条件
if(num==0)t++;
else if(map[x][y]>v&&num==1)t++;
}
if(x+1<=n){//能下走
if(map[x][y]>v)t+=dfs(x+1,y,num-1,map[x][y]);
t%=N;
t+=dfs(x+1,y,num,v);
t%=N;
}
if(y+1<=m){//能右走
if(map[x][y]>v)t+=dfs(x,y+1,num-1,map[x][y]);
t%=N;
t+=dfs(x,y+1,num,v);
t%=N;
}
return dp[x][y][num][v+1]=t;//把下右的结果相加
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>map[i][j];
memset(dp,-1,sizeof(dp));
cout << dfs(1,1,k,-1);
}