[ NAIPC2016 ] C. Greetings! [状压DP + 枚举子集]

题目链接
Your greeting card company makes unique greeting cards. The sizes of these greeting cards vary widely because of the whims of card designers. There are a lot of different types of cards, and each has a specific quantity that you need to manufacture.

Your job is to determine what envelopes to order for these greeting cards. You have a strict limit on the different number of different sizes of envelopes, which may be less than the number of distinct sizes of cards. You need to have envelopes so that every card fits in some envelope, possibly with room to spare, and the amount of waste paper is minimized. Measure the waste paper by the area of the envelope that is in excess of the area of the card, for each card. For example, a 10 \times 410×4 card in a 10 \times 410×4 envelope has no wasted paper, but a 10 \times 410×4 card in a 12 \times 512×5 envelope has waste of 2020. You may not rotate the cards to fit them in the envelopes better.

Suppose that you have 55 types of cards: 10 \times 1010×10 (55 of these), 9 \times 89×8 (1010 of these), 4 \times 124×12 (2020 of these), 12 \times 412×4 (88 of these), and 2 \times 32×3 (1616 of these).

Now, suppose that you can only buy one type of envelope. Since all cards have to fit in that one envelope size, the smallest envelope size you can use is 12 \times 1212×12, with an area of 144144. The wastes by each type of card are 144-10\cdot10 = 44144−10⋅10=44, 144-9\cdot8 = 72144−9⋅8=72, 144-4\cdot12 = 96144−4⋅12=96, 144-12\cdot4 = 96144−12⋅4=96, and 144-2\cdot3 = 138144−2⋅3=138, respectively. The total waste is 44\cdot5+72\cdot10+96\cdot20+96\cdot8+138\cdot16 = 583644⋅5+72⋅10+96⋅20+96⋅8+138⋅16=5836.

Suppose that you can buy 22 types of envelopes. The best you can do is to put the 10 \times 1010×10, 9 \times 89×8 and 12 \times 412×4 cards in 12 \times 1012×10 envelopes, and the 4 \times 124×12 and 2 \times 32×3 cards in 4 \times 124×12 envelopes. That adds up to waste of 18281828.

If you can buy 55 types of envelopes, then you can match one envelope type to each card type, and there’s no waste!

Given a list of card types and the number of types of envelopes you can buy, what is the smallest amount of wasted paper you can achieve?

Input
Each input will consist of a single test case. Note that your program may be run multiple times on different inputs. The first line of the input will consist of two space-separated integers nn and kk (1 \le n, k \le 15)(1≤n,k≤15), where nn is the number of different types of cards, and kk is the maximum number of types of envelopes you can order. Each of the following nn lines will consist of three integers, describing a type of card. The integers are ww, hh and qq (1 \le w, h, q \le 10,000)(1≤w,h,q≤10,000), where ww is the width of the cards of this type, hh is the height of the cards, and qq is the quantity of cards of this type.

Output
Output a single integer, representing the smallest possible total amount of wasted paper.

样例输入
5 1
10 10 5
9 8 10
4 12 20
12 4 8
2 3 16
样例输出
5836
样例输入
5 2
10 10 5
9 8 10
4 12 20
12 4 8
2 3 16
样例输出
1828
样例输入
5 5
10 10 5
9 8 10
4 12 20
12 4 8
2 3 16
样例输出
0
题目来源
The North American Invitational Programming Contest 2016

分析: d p [ I ] [ S ] dp[I][S] dp[I][S] 表示: 最多用 I I I个信封装 S S S的最小浪费.
那么 d p [ I ] [ S ] = m a x ( d p [ I ] [ S ] , d p [ I − 1 ] [ s u b ] + d p [ 1 ] [ S − s u b ] ) , s u b ⊆ S dp[I][S] = max(dp[I][S], dp[I - 1][sub] + dp[1][S - sub]), sub \subseteq S dp[I][S]=max(dp[I][S],dp[I1][sub]+dp[1][Ssub]),subS
Code

 
#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define rep(i, l, r) for(int i = l; i < r; i++)
#define per(i, r, l) for(int i = r; i >= l; i--)
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<"\n"


typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

const int N = (int) 16;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/
void test(){
	int S = 10;
	int sub = S;
	do{
		string s = "";
		int t = sub;
		while(t){
			s += t % 2 + '0';
			t /= 2;
		}
		reverse(s.begin(), s.end());
		cout << s <<"\n";

		sub = (sub - 1) & S;
	}while(sub != S);
}

int w[N], h[N], q[N];
ll dp[N][1 << N];
int main(){
	//test();
	int n, k; 
	while(~scanf("%d%d", &n, &k)) {
		rep(i, 0, n) scanf("%d%d%d", w + i, h + i, q + i);
		memset(dp[0], 0, sizeof(dp[0]));
		rep(i, 0, 1 << n){
			ll mxw = 0, mxh = 0;		
			rep(j, 0, n){
				if(i >> j & 1) {
					mxw = max(mxw, (ll)w[j]);
					mxh = max(mxh, (ll)h[j]);
				}
			}
			rep(j, 0, n){
				if(i >> j & 1){
					dp[0][i] += q[j] * 1ll * (mxw * mxh - w[j] * h[j]);
				}
			}
		}

		rep(I, 1, k){
			rep(S, 0, 1 << n){
				int sub = S;
				dp[I][S] = dp[I - 1][S];
				do{
					dp[I][S] = min(dp[I][S], dp[I - 1][sub] + dp[0][S - sub]);
					sub = (sub - 1) & S;
				}while(sub != S);
				//			dbg(dp[I][S]);
			}
		}
		printf("%lld\n", dp[k - 1][(1 << n) - 1]);
	}	
	return 0;
}

好的,我很乐意为您介绍举子-增量构造法使用C语言的方法。 举子-增量构造法是一种常用的算法技巧,用于生成合的所有子。这种方法的基本思想是从空开始,逐步向子中添加元素,直到包含所有元素。下面是使用C语言实现这种方法的示例代码: ```c #include <stdio.h> void print_subset(int subset[], int size) { printf("{ "); for(int i = 0; i < size; i++) { printf("%d ", subset[i]); } printf("}\n"); } void enumerate_subsets(int set[], int n) { int subset_size = 0; int subset[100]; // 假设最大子大小不超过100 for(int i = 0; i < (1 << n); i++) { // 遍历所有可能的子 subset_size = 0; // 重置子大小 for(int j = 0; j < n; j++) { if(i & (1 << j)) { // 检查第j位是否为1 subset[subset_size++] = set[j]; // 将元素加入子 } } print_subset(subset, subset_size); // 打印当前子 } } int main() { int set[] = {1, 2, 3}; int n = sizeof(set) / sizeof(set[0]); printf("合 {1, 2, 3} 的所有子为:\n"); enumerate_subsets(set, n); return 0; } ``` 这段代码的工作原理如下: 1. 我们定义了一个`enumerate_subsets`函数,它接受一个整数数组和数组的大小作为参数。 2. 在这个函数中,我们使用一个循环来遍历所有可能的子。循环的范围是0到2^n-1,其中n是合的大小。 3. 对于每一个可能的子,我们使用位运算来检查哪些元素应该包含在当前子中。 4. 如果某一位为1,我们将对应的元素添加到子中。 5. 每次生成一个子后,我们调用`print_subset`函数来打印这个子。 6. 在`main`函数中,我们初始化一个合并调用`enumerate_subsets`函数来生成并打印所有子。 这种方法的时间复杂度是O(2^n),其中n是合的大小,因为我们需要生成所有可能的子
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值