gym 101002C Greetings! 狀壓DP

題目鏈接

題目大意:

有n( n <= 15)種賀卡,每種給了尺寸還有數量。可以定製k種信封,用k種信封裝n種賀卡,如果用4*12的信封裝3*3的賀卡,浪費了4*12 - 3*3 = 39面積的紙。求最少的浪費。注意4*12的信封不能裝12*4的賀卡

題目分析:

該問題數據範圍n,k都是15,很容易就想到有比較暴力的做法或者能夠狀壓DP。

我們把目前已經安排上了的賀卡狀態壓縮,設DP[ i ][ j ]表示用 i 個信封,狀態爲 j 的情況下最小的浪費

那麼DP[ i ][ j ] = min( DP[ i ][ j ] , DP[ i - k][x] + DP[ k ][j ^ x])。其中 k 小於 i ,x是 j 的一個非空子集。意思就是用i個賀卡達到狀態j,能夠分成 i - k 個信封達到 x ,再用 k 個信封達到 j ^ x,兩者之和就是 j 。

其中的 k 可以從1到 i - 1,就需要快速地找到所有的 x ,不然複雜度太高了(常數太大了)。
我們可以通過 for(int x = j ; x ; x = (x - 1) & j ) 這麼一個循環來遍歷 j 的所有非空子集。具體正確性可以自行證明。

最後的答案就是DP[ k ][ (1 << n) - 1 ],這樣一來這個問題就解決了。但是注意這個題目時限很長,同時常數大了是過不去的。最好常數小一點或者判斷 k >= n 直接輸出0。

貼的代碼裏面有好多語句寫在同一行用逗號隔開的,好像是常數小一些?有知道的求解答一下謝謝鴨

AC代碼:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = (1 << 15) + 10;
int n,k;
ll dp[20][32800];
ll X[20],Y[20],num[20];
ll que[20],cnt;

int main(){
	ios::sync_with_stdio(false);
	cin>>n>>k;
	for(int i = 0;i < n;i++){
		cin>>X[i]>>Y[i]>>num[i];
	}
	for(int i=1;i<=k;i++)
    for(int j=0;j<1<<n;j++)
        dp[i][j]=1e18+10;
    dp[0][0]=0;
	for(int i = 0;i < (1<<n);i++){
		ll maxx = 0,maxy = 0,cnt = 0, sum = 0;
		int tmp = i, id = 0;
		while(tmp){
			if(tmp & 1){
				maxx = max(maxx,X[id]),maxy = max(maxy,Y[id]),sum += X[id] * Y[id] * num[id],cnt += num[id];
			}
			id++;
			tmp >>= 1;
		}
		dp[1][i] = maxx*maxy*cnt - sum;
	}
	for(int i = 2;i <= k;i++){
		for(int cas = 0;cas < (1 << n);cas++){
			for(int x = cas;x;x = (x-1) & cas){
				for(int k = 1;k < i;k++){
					dp[i][cas] = min(dp[i][cas],dp[i-k][x] + dp[k][cas^x]);
				}
			}
		}
	}
	cout<<dp[k][(1<<n) - 1]<<endl;
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章