今天小編閑的不行,就打開洛谷,隨便一打卡就是大吉,還宜刷題。
正巧上午比賽時有一道背包問題,於是小編默默打開試煉場,瞅准了背包問題(別問我為什么),正所謂自知者明,小編也知道自己很水(建議看背包九講),於是挑了三道題:
在寫之前總得知道什么是背包問題吧,背包問題一般長這樣:
題目:有N件物品和一個容量為V的背包。第i件物品的費用是w[i],價值是value[i]。求解將哪些物品裝入背包可使價值總和最大。
那么如何解這種題目呢,先定義一個數組f[i][j]為當一共有i件物品,背包容量為j時的最大價值。然后就要找狀態轉移方程了,小編以前總認為狀態轉移方程難寫,但是只要一項一項列出來就好了。對於每一個物品,無非就兩種可能:要么選,要么不選。那么先看選,凡是總有回報和代價把,選了之后代價是什么呢?想一想,是不是選了之后背包剩余容量就減少了;那么回報呢?當然是價值增加了唄。但是不選就不一樣了,應為啥也沒干,最大價值和之前一樣,不增不減。這不就出來了嘛,兩種情況如下:
- 選:f[i][j]=f[i-1][j-w[i]]+value[i];
- 不選:f[i][j]=f[i-1][j];
因為題目求的是最大價值,所以兩者中取大的就可以了,得到以下狀態轉移方程:
f[i][j]=max(f[i-1][j-w[i]]+value[i],f[i-1][j]);
有沒有發現什么,我們用了二維數組,雖然時間上已經難以優化,但是空間上還是可以優化成一維數組的,只要同時去掉i的那一個維度就可以了,因為二維數組有太多不需要一直記錄的,直接不斷更新一維數組(滾動數組的方式)就可以了,更改如下:
f[j]=max(f[j-w[i]]+value[i],f[j]);
具體怎么實現,看幾道吧。
先看第一道:
這道題處於秒殺的行列,直接用剛才的方法,把錢數看成是容量,把重要程度*價格看成是價值就好了,直接寫就行,代碼奉上:
1 #include<iostream> 2 using namespace std; 3 long long cost[30000],w[30000],f[30000],ans; 4 int main() 5 { 6 long long n,m; 7 cin>>n>>m; 8 for(int i=1;i<=m;i++) 9 cin>>cost[i]>>w[i]; 10 for(int i=1;i<=m;i++) 11 for(int j=n;j>=cost[i];j--) 12 { 13 if(j>=w[i]) 14 f[j]=max(f[j],f[j-cost[i]]+w[i]*cost[i]); 15 } 16 cout<<f[n]; 17 return 0; 18 }
先來看一下采葯,比上面的還簡單:
把時間看成容量,就可以了,代碼獻上:
1 #include<iostream> 2 using namespace std; 3 int t,m,w[1000],cost[1000],f[1000]; 4 int main() 5 { 6 cin>>t>>m; 7 for(int i=1;i<=m;i++) 8 cin>>w[i]>>cost[i]; 9 for(int i=1;i<=m;i++) 10 for(int j=t;j>=w[i];j--) 11 { 12 f[j]=max(f[j],f[j-w[i]]+cost[i]); 13 } 14 cout<<f[t]; 15 16 return 0; 17 }
最后來看小A點菜:
這道題乍一看沒思路,還按照剛才的思路:要么吃,要么不吃,吃有什么代價,什么回報呢?錢變少了,方案數變多了唄;不吃呢?還是原來的方案數。這樣兩種情況就出來了:
-
f[j-cost[i]];
-
f[j]
;
【注意】情況有變,這一次就不那么簡單了,因為選和不選是兩種不同的方案數,題目求的是一共的方案數,所以不是max,不是min,而是+。
歸根結底長這樣:f[j]=f[j]+f[j-cost[i]];
這樣代碼就出來了,代碼呈上:
1 #include<iostream> 2 using namespace std; 3 int n,m,cost[100000],f[10000]; 4 int main() 5 { 6 cin>>n>>m; 7 for(int i=1;i<=n;i++) 8 cin>>cost[i]; 9 f[0]=1; 10 for(int i=1;i<=n;i++) 11 for(int j=m;j>=cost[i];j--) 12 f[j]=f[j]+f[j-cost[i]]; 13 cout<<f[m]; 14 return 0; 15 }