hdu-6289 尋寶遊戲 dp
阿新 • • 發佈:2018-12-11
題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6289
題意:
給你n*m的格子,每個格子上有一個值,現在你在(1,1),你只能往下和往右走一直到(n,m),並獲得經過的路上的格子的值,你現在有k次機會可以交換兩個不同格子上的權值,問你最後能獲得的最大值為多少。
做法:
dp,真的不是很好想啊....就算知道是dp,知道每一維代表什麼也不是一下子就能寫出來的...思維dp,四位分別代表,走到位置(i,j)時,撿了p個物品和使用了q個物品的最大值。
這個撿了p個和使用了q個一下子有點讓人困惑,其實就是你要保持住這個狀態,便於更新,後兩維有點像是揹包的感覺,看完大佬的部落格後自己的理解是。在往右走的時候,是不做很大改動的,只是儲存住當前這個狀態而已,而當你往下走的時候,你要對下一行左邊你沒有走過的點和當前行往後你也走不了了的點做揹包的更新,先儲存下來這些值並做好預處理,然後當要作轉移的時候,要列舉還需要使用多少次機會(顯然已經用了的和你還要用的是不能超過k次的),再用這k次機會去使用你之前儲存下來的前k個最大的值,然後不斷的作更新,最後你只要列舉在(n,m)這個點,使用次數和撿物品個數相同的dp,找一個最大值即可。
程式碼或多或少有點雷同吧,畢竟是參考著寫的。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int mp[55][55],dp[55][55][25][25],tmp[105],now,n,m,k; bool cmp(int a,int b){return a>b;} void fresh(int &a,int b){if(a<b) a=b;} int main(){ int t; cin>>t; while(t--){ scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); memset(dp,-1,sizeof(dp)); dp[1][1][0][0]=mp[1][1]; dp[1][1][1][0]=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ tmp[0]=now=0; for(int z=1;z<=m;z++){ if(z<j) tmp[++now]=mp[i+1][z]; else if(z>j) tmp[++now]=mp[i][z]; } sort(tmp+1,tmp+1+now,cmp); for(int i=1;i<=now;i++) tmp[i]+=tmp[i-1]; for(int p=0;p<=k;p++){//已經拿了p個,和已經用了q個的最大值 for(int q=0;q<=k;q++){ if(dp[i][j][p][q]==-1) continue; int now=dp[i][j][p][q]; if(j<m){ fresh(dp[i][j+1][p][q],now+mp[i][j+1]); if(p<k) fresh(dp[i][j+1][p+1][q],now); } if(i<n){ for(int z=0;z<=k;z++){ if(z+q<=k){ fresh(dp[i+1][j][p][z+q],now+tmp[z]+mp[i+1][j]); if(p<k) fresh(dp[i+1][j][p+1][z+q],now+tmp[z]); } } } } } } } int ans=dp[n][m][0][0]; for(int i=1;i<=k;i++) ans=max(ans,dp[n][m][i][i]); printf("%d\n",ans); } return 0; }