POJ 2168 Joke with Turtles

Description Special Judge

There is a famous joke-riddlefor children:

Three turtles are crawlingalong a road. One turtle says: "There are two turtles ahead of me."
The other turtle says: "There are two turtles behind me." The thirdturtle says: "There are
two turtles ahead of me and two turtles behind me." How could this havehappened?
The answer is -- the third turtle is lying!


Now in this problem you have n turtles crawling along a road. Some of them arecrawling in a group, so that they do not see members of their group neitherahead nor behind them. Each turtle makes a statement of the form: "Thereare ai turtles crawling ahead of me and bi turtles crawling behind me."Your task is to find the minimal number of turtles that must be lying.
Let us formalize this task. Turtle i has xi coordinate. Some turtles may have thesame coordinate. Turtle i tells the truth if and only if ai is the number ofturtles such that xj > xi and bi is the number of turtles such that xj <xi. Otherwise, turtle i is lying.

Input

The first line of the inputcontains integer number n (1 <= n <= 1000). It is followed by n linescontaining numbers ai and bi (0 <= ai, bi <= 1000) that describestatements of each turtle for i from 1 to n.

Output

On the first line of theoutput file write an integer number m -- the minimal number of turtles that mustbe lying, followed by m integers -- turtles that are lying. Turtles can beprinted in any order. If there are different sets of m lying turtles, thenprint any of them.

Sample Input

5

0 2

0 3

2 1

1 2

4 0

Sample Output

2 1 4

题目简介:有n只乌龟,第i只乌龟说他后边有ai只乌龟,前边有bi只乌龟。乌龟的名次可以重复,但是一个区间上乌龟数不能超过其长度。计算至少出有多少只乌龟说了假话。并说出是哪些乌龟说了假话。

方法:动态规划。若一只海龟说了真话,那么该海龟的位置一定是在区间[ai+1,N-bi]上。若两只海龟区间重叠,则必有一个在撒谎。若多只海龟属于同一区间,它们可能都在说真话,但不能超过区间长度。问题转换为,给出n个区间,第i个区间权为v[i],选出不相交的区间,使得总权尽量大。先按右端点坐标从小到大排序,令p[i]为在区间i左边的且与之无公共点的最大区间编号,设状态f[i]为在前i个区间中可选出区间的最大权和,则状态转移方程为f[i]=max{f[i-1],f[p[i]]+v[i]},说真话海龟的最大数量就是最后一个区间的f值。注意:例子中有ai+1>n-bi的情况。说出这种情况的乌龟一定撒谎了。

 

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;

vector<int> lie;//记录撒谎乌龟的序号
vector<int> truth;//记录说真话乌龟的序号

struct area
{
	int left;//记录左边界
	int right;//记录右边界
	vector<int> v;//保存该区域乌龟的编号
}map[1010];

struct node
{
	int num;//前i个区间一共有多少个乌龟
	int pre;//记录该区间之前的区间号
	int flag;//该区间是否被用过
}f[1010];

int flag[1010][1010];

int cmp(const void *a,const void *b)
{
	struct area *c, *d;
	c = (struct area *)a;
	d = (struct area *)b;
	return c->right - d->right;
};

int main()
{
	int n;
	int a, b;
	while(scanf("%d",&n)!=EOF)
	{
		memset(f,0,sizeof(f));
		memset(map,0,sizeof(map));
		memset(flag,-1,sizeof(flag));
		lie.clear();
		truth.clear();

		int count = 0;//记录区间数
		
		for(int i = 1;i<=n;i++)
		{
			scanf("%d%d",&a,&b);

			a++;
			b = n - b;

			if(a>b)
			{
				lie.push_back(i);
			}//出现这种情况直接放入撒谎乌龟里

			else if(flag[a][b]==-1)//没有这个区间出现
			{
				map[count].left = a;//区间的左边值
				map[count].right = b;//区间的右边值
				map[count].v.push_back(i);//将该乌龟的序列值放进该区间
				flag[a][b] = count;//该区间出现过
				count++;
			}
			else
			{
				if(b - a + 1 > map[flag[a][b]].v.size())
				{
					map[flag[a][b]].v.push_back(i);
				}//如果该区间上的乌龟数小于区间长度,那么就还可以继续往里边加入
			}
		}

		qsort(map,count,sizeof(map[0]),cmp);//将区间按右边值从小到大排序

		int p[1010];
		memset(p,-1,sizeof(p));

		//找到与其没有公共点的最大的左边值区间
		for(int i = 0;i<count;i++)
		{
			for(int j = i-1;j>=0;j--)
			{
				if(map[i].left>map[j].right)
				{
					p[i] = j;
					break;
				}
			}
		}


		for(int i = 1;i<=count;i++)
		{
			f[i].num = (i==1 ? 0 : f[i-1].num);
			f[i].flag = (i==1 ? 1 : 0);
			f[i].pre = (i==1 ? 0 : i-1);

			if(p[i-1]==-1)//表示其前面没有区间
			{
				if(f[i].num < map[i-1].v.size())
				{
					f[i].num = map[i-1].v.size();
					f[i].flag = 1;
					f[i].pre = 0;
				}
			}
			
			else
			{
				if(f[i].num < f[p[i-1]+1].num + map[i-1].v.size())
				{
					f[i].num = f[p[i-1]+1].num + map[i-1].v.size();
					f[i].flag = 1;
					f[i].pre = p[i-1]+1;
				}
			}
		}

		printf("%d",n - f[count].num);


		int i = count;
		while(i)
		{
			if(f[i].flag==1)//该区间被利用过
			{
				for(int j = 0;j<map[i-1].v.size();j++)
				{
					truth.push_back(map[i-1].v[j]);//把该区间的乌龟序列号存入说真话里
				}
			}
			i = f[i].pre;//指向之前区间
		}

		for(i = 1;i<=n;i++)
		{
			int flag1 = 0;
			for(int j = 0;j<truth.size();j++)
			{
				if(i==truth[j])
				{
					flag1 = 1;
					break;
				}
			}
			//遍历是否是说真话的乌龟,如果不是就进入if
			if(flag1==0)
			{
				int flag2 = 0;
				for(int j = 0;j<lie.size();j++)
				{
					if(i==lie[j])
					{
						flag2 = 1;
						break;
					}
				}//如果说假话里面没有出现过该序列号,就加入
				if(flag2==0)
				{
					lie.push_back(i);
				}
			}
		}
		
		
		sort(lie.begin(),lie.end());

		for(i = 0;i<lie.size();i++)
		{
			printf(" %d",lie[i]);
		}
		printf("\n");
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值