动态规划(5) 2168 Jokes With Turtles

来源:互联网 发布:省市区镇四级联动sql 编辑:程序博客网 时间:2024/05/21 17:52

Description

There is a famous joke-riddle for children:
Three turtles are crawling along a road. One turtle says: "There are two turtles ahead of me."
The other turtle says: "There are two turtles behind me." The third turtle says: "There are
two turtles ahead of me and two turtles behind me." How could this have happened?
The answer is -- the third turtle is lying!

Now in this problem you have n turtles crawling along a road. Some of them are crawling in a group, so that they do not see members of their group neither ahead nor behind them. Each turtle makes a statement of the form: "There are 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 the same coordinate. Turtle i tells the truth if and only if ai is the number of turtles 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 input contains integer number n (1 <= n <= 1000). It is followed by n lines containing numbers ai and bi (0 <= ai, bi <= 1000) that describe statements of each turtle for i from 1 to n.

Output

On the first line of the output file write an integer number m -- the minimal number of turtles that must be lying, followed by m integers -- turtles that are lying. Turtles can be printed in any order. If there are different sets of m lying turtles, then print any of them.

Sample Input

50 20 32 11 24 0

Sample Output

2 1 4

 

题目大意:

有N只可爱的小海龟在赛跑。询问每只小海龟它是第几名,它会回答你两个数,ai,bi,分别表示在它前面的小海龟数和在它后面的小海龟数。
接着你发现其中有些小海龟对你撒了谎,因为根据他们的说法你根本没法给他们排队!
但是你是善良的,你不希望有很多小海龟在撒谎,想找出最少有哪几只小海龟在撒谎。
(注意:小海龟的名次可能是并列的!)

分析:

若一只海龟说了真话,那么该海龟的位置一定是在区间[ai+1,n-bi] 上。
若有k只海龟选择了相同的区间 ,则根据并列关系,该区间最多能同时拥有min{n-ai-bi,k}只海龟(多出来的海龟肯定是说谎的,预先排除掉)。
可以计算出每个区间最多能有多少只海龟,把数值看做区间的“权”。则问题转化为,在一些带权区间中,选出权和最大的区间,使它们之间不能互相重叠。

算法:

算出每个出现过的区间的“权”vi ,接下来的算法就是动态规划了。
先按右端点坐标从小到大排序,令pi 为在区间 i左边的且与之无公共点的最大区间编号,
设状态f[i] 为在前i 个区间中可选出区间的最大权和,则状态转移方程为f[i]=max(f[i-1],f[p[i]]+v[i]) ,说真话海龟的最大数量就是最后一个区间的f(i) 值。(转载的,个人表示能力不足,翻译不了这么形象。。。)


这个题还是DP题,个人觉得计算出最少说谎的乌龟数比较简单,只需要一步一步按上面的步骤做就可以了。还是那个记录路径的问题,这次和上次的倒水问题不一样,这次也是求救了学长,学会了倒着推的方法,因为前面对相同区间的合并的时候已经将乌龟编号也已经合并记录,最后用状态f[i]所表示的最大权和也已经都记录了,所以只需要判断f[i]是由f[i-1]还是f[p[i]]+v[i]得到的,然后分情况记录说谎的乌龟编号就行了,代码也很好实现,耶!

#include<stdio.h>#include<string.h>#include<algorithm>#define MAX 1010using namespace std;int a[MAX],b[MAX],p[MAX],f[MAX],num[MAX],ji=1;//a前面的数目,b后面的数目,p[i]为在区间 i左边的且与之无公共点的最大区间编号.//f[i]为在前i 个区间中可选出区间的最大权和,num用来记录说谎的乌龟的编号。ji控制num[ji]的移动。struct node{int s,e;//s,e区间的始点和终点。int count,rank;//count区间的权,rank编号int men[MAX];//记录区间里的所有编号bool flag;//标记}sec[MAX];bool cmp(node x,node y){if(x.e==y.e)return x.s<y.s;else    return x.e<y.e;}int main(){int n,i,j,k;memset(sec,0,sizeof(sec));memset(num ,0,sizeof(num));scanf("%d",&n);for(int i=1;i<=n;i++)//定义每个区间。{sec[i].count = 0;sec[i].rank = i;sec[i].flag = true;scanf("%d %d",&a[i],&b[i]);if(a[i] + b[i] + 1 > n || a[i]+1>n-b[i])        {sec[i].flag = false;sec[i].s = sec[i].e = sec[i].count =n+1;num[ji++]=i;}else{     sec[i].s = a[i]+1;     sec[i].e = n-b[i]; sec[i].count++;}}for( j=1;j<=n;j++)//求每个区间上的权。{int y = 1;sec[j].men[y++] = sec[j].rank;for(int k=j+1;k<=n;k++){if(sec[j].flag && sec[k].flag && sec[j].s==sec[k].s && sec[j].e==sec[k].e)//保证没有被遍历过的相同区间{                    if(sec[j].count>=(sec[j].e-sec[j].s+1))//如果超出区间可容纳的数目,超出之后的所有都看做是在撒谎。{num[ji++] = sec[k].rank;}else{                                  //否则,区间内总数加1,并记录序号。       sec[j].count +=1;   sec[j].men[y++] = sec[k].rank;}sec[k].flag = false;//不管超不超出,都将其标记,并设置成不可达到的值。sec[k].s = sec[k].e = n+1;}}};sort(sec+1,sec+1+n,cmp);//cmp按照区间的右边界小大顺序排列。memset(p,0,sizeof(p));f[0]=0;                          //初始化f[1] = sec[1].count;p[1]=0;int t=0;for( i=1;sec[i].e<n+1 && i<=n;i++){for( j=i-1;j>0;j--){           if(sec[i].s>sec[j].e)   { p[i] = j;//求不相交的左边最大的编号 break;   }} f[i]=(f[i-1]>=f[p[i]] + sec[i].count)?f[i-1]:f[p[i]] + sec[i].count; //两种情况     t=i;}    for(j=t;j>0;)//判断哪些乌龟是说谎的!!!{if(f[j]==f[j-1]){for(i=1;sec[j].men[i]!=0;i++)num[ji++] = sec[j].men[i];    j = j-1;}else{for(k=p[j]+1;k<j;k++){for(i=1;sec[k].men[i]!=0;i++)num[ji++] = sec[k].men[i];}j=p[j];}}printf("%d ",n-f[t]);for(int j=1;j<ji;j++)printf("%d ",num[j]);printf("\n");return 0;}


原创粉丝点击