USACO2013 Nov. Gold T3,一道集合DP

网上关于集合动态规划的题目好像不多?

关于集合DP的题目基本没见到过多少...但是USACO 2013 Nov. Gold T3居然直接考了个裸的......

于是就怒学一发...


集合DP,意思是我们定义状态的时候使用集合.

一般我们定状态,比如LIS(最长上升子序列),

     d[i]: 以第i个元素结尾的最长上升序列长度.

再比如0/1背包,

     d[i][j]: 考虑前i件物品,用掉了容量j所能达到的最大价值

或者 d[i][j]: 考虑前i件物品,背包容量上限为j,所能达到的最大价值


适用范围:

1.集合DP是指数级算法,一般有n<=20,将n!的复杂度优化至2^n;

2.集合DP想要求出一个依赖于集合中元素顺序的结果,但是判定方案是否可行或者状态转移不需要得到原集合中元素顺序.



集合dp呢就是"把集合当下标".具体的看下边....


例题在此:  http://www.usaco.org/index.php?page=viewproblem2&cpid=348

题目大意是,给你n<=16个硬币,每个硬币有一个面值c[i],要按照给定先后顺序购买m<=100000个物品,所有物品的价格a[i]由输入给出,不能找零,求最多能留下多少钱(价值).

n<=16是一个很明显的提示.这告诉我们2^n的算法能做.


考虑最优的购买方案.

我们肯定是从n个硬币中选出若干个,然后按照某种排列依次购买物品.

直接枚举硬币的选取情况(某个硬币选或不选),有2^n中可能.

然后我们试着快速地判断这个选取硬币的方案是不是最优的.

怎么评判方案的优劣呢? 显然,剩余的硬币价值之和越大,方案就越好.

对于每种选取硬币的方案S,如果我们能够快速判断它是否能购买完所有物品,那么就能在O(1)时间算出S能带来的最大剩余价值.

于是,我们的任务变成,快速判断S能否购买完所有物品.

于是集合DP登场.

还是枚举S.

注意到他给出的一个小小的条件我们没有用到:物品必须按顺序买,一旦买了物品k,那么物品k-1,物品k-2...物品1都得买.

于是乎...

状态: d[S]: S最多能够买到哪件物品.(S所能取得的最大k)

转移: d[S]=max( d[S\p]+用硬币p在之后的中按顺序购买,最多能买到的物品数 );

读起来比较绕.

我们用S\p购买到了第k=d[S\p]个物品,那么我们接下来就一定要用硬币p购买之后的物品k+1,物品k+2..直到不能买为止.

显然,如果把所有物品买完了,说明S是可行的.

最后一个小问题,边界.

边界: d[S]=0,当且仅当S为空(不使用任何硬币).

整个DP就完成了.....

有一个小优化.用硬币p购买物品k+1,物品k+2...直到h-1,h.我们想要快速找出这个h,毕竟m=100000,扫一遍的话速度够呛.

这个直接二分h的位置即可.


接下来是照例帖代码....

直接用map做的记忆化搜索.

最大数据点跑620ms. USACO评测机真心快....

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <cstring>
#include <fstream>

#include <cmath>

#include <vector>
#include <queue>
#include <stack>

typedef long long ll;
typedef unsigned long long ull;
typedef long double db;

using namespace std;

ifstream fin("nochange.in");
ofstream fout("nochange.out");

int n,m;

int a[101000];
int __presum[101000];
int*presum=__presum+1;
int c[20];

class Set
{
	public:
	
	bool t[17];
	Set(){}
	Set(bool h)
	{
		for(int i=0;i<n;i++)
		t[i]=h;
	}
	bool empty()
	{
		for(int i=0;i<n;i++)
		if(t[i]) return false;
		return true;
	}
	
	bool&operator[](int f) { return t[f]; }
	
	Set operator-(int k)
	{
		Set c(*this);
		c[k]=false;
		return c;
	}
	
	bool operator<(const Set x) const
	{
		for(int i=0;i<n;i++)
		if(x.t[i]!=t[i]) return t[i]<x.t[i];
		
		return false;
	}
	
};

int res=-1;

map<Set,int> g;

int DFS(Set x)
{
	if(x.empty())
		return 0;
	
	if(g.find(x)!=g.end()) return g[x];
	
	int mx=0;
	for(int i=0;i<n;i++)
	if(x[i]!=0)
	{
		int k=DFS(x-i);
		mx=max(mx,(int)(upper_bound(presum,presum+m,presum[k-1]+c[i])-presum));
	
		if(mx==m)
		{
			int ut=0;
			for(int i=0;i<n;i++)
			if(x[i]==0) ut+=c[i];
			res=max(ut,res);
		}
	}
	
	return g[x]=mx;
}

int main()
{
	fin>>n>>m;
	
	for(int i=0;i<n;i++)
	fin>>c[i];
	
	for(int i=0;i<m;i++)
	fin>>a[i];
	
	presum[-1]=0;
	for(int i=0;i<m;i++)
	presum[i]=presum[i-1]+a[i];
	
	DFS(Set(true));
	
	fout<<res<<endl;
	
	return 0;
}


位运算优化的代码.最大的数据点跑了60ms.

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <cstring>
#include <fstream>

#include <cmath>

#include <vector>
#include <queue>
#include <stack>

typedef long long ll;
typedef unsigned long long ull;
typedef long double db;

using namespace std;

ifstream fin("nochange.in");
ofstream fout("nochange.out");

int n,m;

int a[101000];
int __presum[101000];
int*presum=__presum+1;
int c[20];

int res=-1;

int g[145000];

int DFS(int x)
{
	if(x==0)
		return 0;
	
	if(g[x]!=-1) return g[x];
	
	int mx=0;
	int p=1;
	for(int i=0;i<n;i++,p<<=1)
	if((x&p)!=0)
	{
		int k=DFS(x-p);
		mx=max(mx,(int)(upper_bound(presum,presum+m,presum[k-1]+c[i])-presum));
	
		if(mx==m)
		{
			int ut=0;
			int h=1;
			for(int i=0;i<n;i++,h<<=1)
			if((x&h)==0) ut+=c[i];
			res=max(ut,res);
		}
	}
	
	return g[x]=mx;
}


int main()
{
	fin>>n>>m;
	
	for(int i=0;i<n;i++)
	fin>>c[i];
	
	for(int i=0;i<m;i++)
	fin>>a[i];
	
	presum[-1]=0;
	for(int i=0;i<m;i++)
	presum[i]=presum[i-1]+a[i];
	
	int p=0;
	for(int i=0;i<n;i++) p=p<<1|1;
	
	for(int i=0;i<=p;i++) g[i]=-1;
	
	DFS(p);
	
	fout<<res<<endl;
	
	return 0;
}

另外USACO2014 Dec. Gold T1 也是集合DP,必须压位,跑了200ms.记得我调了好久才莫名其妙地干掉唯一一个WA点...





原文地址:https://www.cnblogs.com/DragoonKiller/p/4295955.html