POJ-2923 Relocation---01背包+狀態壓縮
阿新 • • 發佈:2018-04-12
火車 不能 tor 方程 lse 基礎知識 pro eof ++
狀態轉移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k為state[i],1<=j<=(1<<n)-1])。
題目鏈接:
https://vjudge.net/problem/POJ-2923
題目大意:
有n個貨物,給出每個貨物的重量,每次用容量為c1,c2的火車運輸,問最少需要運送多少次可以將貨物運完
思路:
第一次做狀態壓縮(狀態壓縮基礎知識傳送門)
本題的解題思路是先枚舉選擇若幹個時的狀態,總狀態量為1<<n,判斷這些狀態集合裏的那些物品能否一次就運走,如果能運走,那就把這個狀態看成一個物品。預處理完能從枚舉中找到tot個物品,再用這tol個物品中沒有交集(也就是兩個狀態不能同時含有一個物品)的物品進行01背包,每個物品的體積是state[i](state[i]表示一次可以運完狀態i的物品,i的二進制表示i這個狀態的物品),價值是1,求包含n個物品的最少價值也就是dp[(1<<n)-1](dp[i]表示狀態i需要運的最少次數)。
狀態轉移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k為state[i],1<=j<=(1<<n)-1])。
1 #include<iostream> 2 #include<vector> 3 #include<queue> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdio> 7 #include<set> 8 #include<cmath> 9 using namespacestd; 10 typedef pair<int, int> Pair; 11 typedef long long ll; 12 const int INF = 0x3f3f3f3f; 13 const int maxn = 2000+10; 14 int T, n, m1, m2; 15 int a[20], cnt[maxn], dp[maxn], tot, cases; 16 bool vis[maxn]; 17 bool judge(int x) 18 { 19 int sum = 0; 20 memset(vis, 0, sizeof(vis));//vis[i]=1表示m1的車子中可以湊出體積為i的物品21 vis[0] = 1; 22 for(int i = 0; i < n; i++) 23 { 24 if(x & (1 << i))//第i件物品存在 25 { 26 sum += a[i]; 27 for(int j = m1; j >= a[i]; j--) 28 if(vis[j - a[i]])vis[j] = 1;//此處必須是逆序,因為更新vis[j]的時候要用到vis[j-a[i]],和01背包是一樣的 29 } 30 } 31 for(int i = 0; i <= m1; i++) 32 { 33 if(vis[i] && sum - i <= m2)//確保全部物品可以一次性放在兩個車子裏面 34 return true; 35 } 36 return false; 37 } 38 void init() 39 { 40 memset(dp, INF, sizeof(dp)); 41 dp[0] = 0; 42 for(int i = 1; i < (1 << n); i++) 43 { 44 if(judge(i)) 45 { 46 cnt[tot++] = i; 47 } 48 } 49 } 50 int main() 51 { 52 cin >> T; 53 while(T--) 54 { 55 cin >> n >> m1 >> m2; 56 tot = 0; 57 for(int i = 0; i < n; i++)cin >> a[i]; 58 init();/* 59 for(int i = 0; i < tot; i++) 60 cout<<cnt[i]<<endl;*/ 61 for(int i = 0; i < tot; i++)//枚舉物品 62 { 63 for(int j = (1 << n) - 1; j >= 0; j--)//逆序枚舉狀態也是因為dp[j]的更新需要先用到dp[j-***] 64 { 65 if(dp[j] == INF)continue; 66 if((j & cnt[i]) == 0)//兩者無交集 67 dp[j | cnt[i]] = min(dp[j | cnt[i]], dp[j] + 1); 68 //dp[j | cnt[i]]表示j這個狀態加上第i件物品的值,可以從dp[j]+1推過去 69 } 70 } 71 printf("Scenario #%d:\n", ++cases); 72 printf("%d\n\n", dp[(1<<n) - 1]); 73 74 } 75 }
POJ-2923 Relocation---01背包+狀態壓縮