【题解】[USACO12MAR]Cows in a Skyscraper G

题目链接

题目大意:给定一个集合(S),给一个限制条件(P),要求划分集合,使得每一个子集(Ain S),(A)满足限制条件(P),且划分总数最小。

注意到数据范围(n<=18).

第一感状压。

搜索不想写,于是(dp).

原本设计的状态是(f[i][j])表示当前状态为(i),枚举到第(j)件物品的最大容量,(g[i][j])表示状态为(i),枚举到(j)的最小划分数。然而太复杂了,没有必要,写崩了搞了(16pts).

换一种思路。设计(f[i])表示状态为(i)的最大容量,(g[i])表示状态为(i)的最小划分。显然,以(f)为第一关键字转移,若可以装,则优先转移该状态。遵循贪心策略,当前背包只要可以装,那就装。

注意的是,需要更新背包数量的时候,注意要回归到(w-a[j])

代码:

#include<bits/stdc++.h>
using namespace std;
//设计g[i]表示状态为i的背包最大容量 
//f[i]表示状态为i的最小划分数
//
int n,w,ans=500;
int a[18]; 
int f[1<<18],g[1<<18];
int main(){
	scanf("%d%d",&n,&w);
	for(int i=0;i<n;++i)scanf("%d",&a[i]);
	sort(a,a+n);
	memset(f,0x3f,sizeof(f));
	g[0]=w;f[0]=1;
	for(int i=0;i<(1<<n);++i){
		for(int j=0;j<n;++j){
			if(i&(1<<j))continue;
			if(g[i]>=a[j]){
				if(f[i]<=f[i|(1<<j)]){
					f[i|(1<<j)]=f[i];
					g[i|(1<<j)]=max(g[i|(1<<j)],g[i]-a[j]);
				}
			}
			else{
				if(f[i]+1<=f[i|(1<<j)]){
					f[i|(1<<j)]=f[i]+1;
					g[i|(1<<j)]=max(g[i|(1<<j)],w-a[j]);
				}
			}
		}
	}	
	int T=(1<<n)-1;
	cout<<f[T]<<endl;
	return 0;
}

实现上可能有些许差异。注意两个数组代表的意义。

原文地址:https://www.cnblogs.com/h-lka/p/12422664.html