1. 程式人生 > 實用技巧 >揹包dp(01)

揹包dp(01)

01揹包

http://acm.hdu.edu.cn/showproblem.php?pid=2546

餘額為體積;

01揹包比較明顯;

因為是>=5時才能消費,所以預留5的空間,計算出在餘額為m-5的情況下,所能花費的最大價錢;

記住,因為只要>=5,不管菜多貴,都能買;所以我們希望5元時買的菜最貴;所以排序,只計算前n-1個菜獲得的最大值,再加上最後一個即可;

還有個特判m<5時

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x7f7f7f;
int t,n,m;
int dp[1011],val[1010]; signed main() { while(scanf("%d",&n)!=EOF) { if(n==0) break; memset(val,0,sizeof(val)); for(int i=1;i<=n;i++) cin>>val[i]; memset(dp,0,sizeof(dp)); scanf("%d",&m); if(m<5) {cout<<m<<endl;continue;} sort(val
+1,val+1+n); dp[0]=0; for(int i=1;i<=n-1;i++) { for(int j=m-5;j>=val[i];j--) { if(dp[j]<dp[j-val[i]]+val[i]){ dp[j]=dp[j-val[i]]+val[i]; } } } cout<<m-(dp[m-5]+val[n])<<endl; } }

間接求值

http://acm.hdu.edu.cn/showproblem.php?pid=2955

有小數,很明顯,p不能作為陣列下標的值;

如果搶劫銀行最多能搶x,對於搶這麼多錢x有個被抓的概率px;

只要px<=p即可;

而對於不被抓的概率,比較難算,因為不具獨立性;

比如兩個搶劫事件A,被抓概率PA=0.02,B,PB=0.03;

而被抓的概率=PA*(1-PB)+PB*(1-PA)+PA*PB

不被抓被抓的概率=(1-PA)*(1-PB);

所以dp【j】表示不被抓的概率

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x3f;
int t,n;
int val[1010];
double pro[1010],dp[10101];//dp代表不被發現的概率
signed main()
{
   cin>>t;
   while(t--)
   {
       double p;
       scanf("%lf %d",&p,&n);
       int sum=0;
       memset(dp,0,sizeof(dp));
       memset(val,0,sizeof(val));
       memset(pro,0,sizeof(pro));
       for(int i=1;i<=n;i++)
       {
           scanf("%d %lf",&val[i],&pro[i]);
           sum+=val[i];
       }
       dp[0]=1;
       for(int i=1;i<=n;i++)
       {
           for(int j=sum;j>=val[i];j--)
            dp[j]=max(dp[j],dp[j-val[i]]*(1.0-pro[i]));
       }
       for(int j=sum;j>=0;j--)
       {
           if(dp[j]>=(1.0-p)){
                cout<<j<<endl;break;
           }
       }
   }
}

揹包問題中求次優解,第K優解的問題

部落格:

https://blog.csdn.net/yandaoqiusheng/article/details/84782655

Bone Collector II

http://acm.hdu.edu.cn/showproblem.php?pid=2639

揹包又不是求最值,對於求k大值,dp[v][k]表示容量v時第k大值;

dp[j][k]可以由dp【j-v【i】】【h】+val【i】和dp[j][h]這兩個轉化過來;

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x3f;
map<int,int> mp;
int t,n;
int val[1010],dp[1010][40],v[1010],a[1010],b[1010];
signed main()
{
   cin>>t;
   while(t--)
   {
       int n,m,k,sum=0;
       cin>>n>>m>>k;
       memset(dp,0,sizeof(dp));
       for(int i=1;i<=n;i++) cin>>val[i];
       for(int i=1;i<=n;i++) cin>>v[i];
       for(int i=1;i<=n;i++)
       {
           for(int j=m;j>=v[i];j--)
           {
               int l=1,r=1;
               for(int h=1;h<=k;h++)
               {
                        a[h]=dp[j][h];//a,b兩個陣列記錄所有可能的數值
                        b[h]=dp[j-v[i]][h]+val[i];
               }
               int p=1;
               a[k+1]=-1,b[k+1]=-1;//合併兩個序列
               while(p<=k&&(a[l]!=-1||b[r]!=-1)){//找出前k個
                 if(a[l]>b[r]) dp[j][p]=a[l],l++;
                 else dp[j][p]=b[r],r++;
                 if(dp[j][p]!=dp[j][p-1]) p++;
               }
           }
       }
       cout<<dp[m][k]<<endl;
   }
}