[置顶] POJ2168 Joke with Turtles

5
0 2
0 3
2 1
1 2
4 0
意为有5只乌龟
第一只乌龟说前面有0只乌龟,后面有2只乌龟
第二只乌龟说前面有0只乌龟,后面有3只乌龟
这些乌龟中一定有撒谎的,否则会发生冲突
对每只乌龟,[ai,bi]表示前面有ai只乌龟,后面有bi只乌龟
所以其位置一定为[ai+1,n-bi]这个区间内,对于例子
[1,3]
[1,2]
[3,4]
[2,3]
[5,5]
第一只乌龟和第二只乌龟的区间重叠,则第一只乌龟和第二只是不能同时选择的,而完全重复是允许的,因为允许并列
问题变成求不重叠的区间集合的最大区间数,完全重复的两个可认为是一个权为2的区间
注意[1,3]这个区间的最大就是3

然后就是一个区间DP问题了

此题有些trick

比如ai+1>n-bi这种样例也有

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
class Interval
{
public:
	int L;
	int R;
	vector<int>num;//这个区间里的乌龟的编号
}a[1100];//区间
bool operator<(Interval a,Interval b)
{
	return a.R<b.R;
}
vector<int>ans;
int Hash[1100][1100];
int d[1100];
//d[i]表示前i个区间能选取的最大权值
//d[i]=d[i-1]      不选取第i个区间
//d[i]=d[p[i-1]+1]+a[i-1]              选取第i个区间
int p[1100];
int n;
bool flag[1100];//flag[i]表示d[i]是否经过d[p[i-1]]计算而来
void Run()//求解DP
{
	//首先计算p数组
	memset(p,-1,sizeof(p));
	for(int i=0;i<n;i++)
	{
		for(int j=i-1;j>=0;j--)
		{
			if(a[j].R<a[i].L)
			{
				p[i]=j;
				break;
			}
		}
	}
	//p数组计算完成,若没有合法的p,则为-1
	d[0]=0;//前0个区间的最大权值是0
	for(int i=1;i<=n;i++)//d[i]表示前i个区间的最大值
	{
		d[i]=d[i-1];
		if(p[i-1]==-1)
		{
			d[i]=max(d[i],(int)a[i-1].num.size());
		}
		else
		{
			d[i]=max(d[i],(int)a[i-1].num.size()+d[p[i-1]+1]);
		}
		if(d[i]!=d[i-1])//不是由d[i-1]计算而来
		{
			flag[i]=true;
		}
	}
}
void Insert(int x)
{
	for(vector<int>::iterator i=a[x].num.begin();i!=a[x].num.end();i++)
	{
		ans.push_back(*i);
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	while(~scanf("%d",&n))
	{
		int tn=n;//保存一开始的n
		memset(flag,false,sizeof(flag));
		int m=0;//计算区间个数
		ans.clear();//结果数组
		memset(Hash,-1,sizeof(Hash));//映射到a数组
		for(int i=0;i<n;i++)
		{
			int ai,bi;
			scanf("%d%d",&ai,&bi);
			ai++;
			bi=n-bi;
			if(ai>bi)
			{
				ans.push_back(i+1);
				continue;
			}
			if(Hash[ai][bi]==-1)//没有出现过这个区间
			{
				a[m].L=ai;
				a[m].R=bi;
				a[m].num.clear();
				a[m++].num.push_back(i+1);
				Hash[ai][bi]=m-1;
			}
			else//已经出现了
			{
				if(bi-ai+1==a[Hash[ai][bi]].num.size())//此区间已经饱和
				{
					ans.push_back(i+1);//一定说谎
				}
				else
				{
					a[Hash[ai][bi]].num.push_back(i+1);
				}
			}
		}
		n=m;
		//现有n个区间,选取不重叠的区间的子集使权最大
		sort(a,a+n);
		Run();
		//dp[n]即为前n个区间获取的最大权值,最大乌龟数
		printf("%d",tn-d[n]);
		int now=n;
		while(now>0)
		{
			if(flag[now])//d[p[now-1]+1]->d[now]
			{
				for(int i=p[now-1]+1;i<now-1;i++)
				{
					Insert(i);//
				}
				now=p[now-1]+1;
			}
			else//d[now-1]->d[now]
			{
				Insert(now-1);
				now--;
			}
		}
		sort(ans.begin(),ans.end());
		for(vector<int>::iterator i=ans.begin();i!=ans.end();i++)
		{
			printf(" %d",*i);
		}
		printf("\n");
	}
	return 0;
}


你可能感兴趣的:(动态规划)