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

题目链接

题目大意:给定一个集合\(S\),给一个限制条件\(P\),要求划分集合,使得每一个子集\(A\in 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;
}

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

posted @ 2020-03-05 20:59  Refined_heart  阅读(216)  评论(0编辑  收藏  举报