Greetings!(列舉子集+dp)
阿新 • • 發佈:2018-12-13
題意:
給n個信(信封有長寬和數目),問在找最多k種信封型別的時候,最少浪費多少紙
思路:
看了看題解,深以為然,這個思路很巧妙,也許是我太菜,沒細想。
我們首先二進位制列舉計算出,每種狀態下,這些信合用同一種信封時的浪費數目。
然後通過列舉子集,dp[ i ] [ j ] 代表 i 種信封,現在已經裝了 j 集合的信封的最小浪費數。
很顯然,對於每種ij 我們從j的子集種去獲取最小值,那麼就是列舉 j 的子集,然後從之前已經退出的 dp[i-1] [ prej ] 中獲得最小答案。
具體實現看程式碼,感覺思路很清晰很巧妙。
狀壓果然優秀。
程式碼:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <iostream> #define ll long long using namespace std; const int maxn = (1<<15)+20; ll dp[20][maxn]; int W[maxn],H[maxn]; ll Q[maxn],C[maxn],cost[maxn]; int w[maxn],h[maxn],q[maxn]; int main() { int n,k; scanf("%d%d",&n,&k); for(int i=0; i<n; i++) { scanf("%d%d%d",&w[i],&h[i],&q[i]); } for(int i=0; i<(1<<n); i++) { for(int j=0; j<n; j++) { if(i&(1<<j)) { W[i] = max(W[i],w[j]); H[i] = max(H[i],h[j]); Q[i] +=q[j]; C[i] +=((long long)w[j]*h[j]*q[j]); } } cost[i] = (long long)W[i]*H[i]*Q[i] - C[i]; // cout<<cost[i]<<" "<<W[i]<<" "<<C[i]<<endl; } const ll inf = 1e12*20; for(int i=0; i<=k;i++) for(int j=0; j<(1<<n); j++) dp[i][j] = inf; dp[0][0] = 0; for(int i=1; i<=k; i++) for(int j=0; j<(1<<n); j++) { ll cnt = inf; for(int k=j; k>0; k = ((k-1)&j)) { cnt = min(cnt,dp[i-1][j-k]+cost[k]); } // if(j==(1<<n)-1) // cout<<cnt<<endl; dp[i][j] = cnt; } ll ans = inf; int d = (1<<n)-1; for(int i=1; i<=k; i++) ans = min(ans,dp[i][d]); printf("%lld\n",ans); return 0; }