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;
}

 

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