!HDU 4293 排队分组说谎问题-dp-(不相交区间的最大个数)

时间:2021-05-05 15:13:37

题意:一群人排队回家,分成很多不同的小团体,现在每个人都说一句自己的团体前面和后面分别有多少人,有些是真话有些是假话,求最大可能说真话的人数

分析:

最大可能说真话的人数也就是求不冲突的信息最多有多少。

一个人说的他的团队前面有多少人后面有多少人,就可以知道他的团队有多少人(有可能是假话)。

这道题画出示意图很关键,数轴表示法mark,这个博客的数轴图就很直观,不过最后一组有点问题,应该有7个红色,但并不影响对题意的理解

通过观察数轴,发现题目就是求不相交的合法区间的最大个数。合法就是说相同话的人的个人不能超过根据他的信息得到的他所在团队的总人数,因为说相同话的人一定属于一个团队。那么问题就转化为了摞箱子问题:

状态:dp[i]前i个区间(也就是团队)的不相交合法区间数num[i],初始为本区间的合法人数;

转移:dp[i]=max(dp[i],dp[j]+num[i])

num[i]就是处理掉本区间多了的人数,这里直接放在结构体数组里dp[i].num了,本质是一样的。

这题的数轴法,转换成求不相交区间的个数是重点。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;
struct node{
	int l,r,num;
}dp[505];
int a[505],n;
bool cmp(node a,node b)
{
	if(a.l!=b.l) return a.l<b.l;
	else return a.r<b.r;
}
int main()
{
	while(scanf("%d",&n)!=EOF){
		int k=0;
		int a,b;
		int nn=n;
		while(nn--){
			scanf("%d%d",&a,&b);
			if(a+b<n){
				dp[k].l=a;
				dp[k].r=n-b;
				dp[k++].num=1;
			}
		}
		sort(dp,dp+k,cmp);
		int tot=0;
		for(int i=1;i<k;i++){
			if(dp[i].l==dp[tot].l&&dp[i].r==dp[tot].r){
				dp[tot].num++;
				dp[tot].num=min(dp[tot].num,dp[tot].r-dp[tot].l);
			}
			else{
				dp[++tot].l=dp[i].l;
				dp[tot].r=dp[i].r;
				dp[tot].num=1;
			}
		}
		int ans=0;
		for(int i=1;i<=tot;i++){
			int tmp=dp[i].num;
			for(int j=0;j<i;j++){
				if(dp[j].r<=dp[i].l)
				    dp[i].num=max(dp[i].num,dp[j].num+tmp);
			}
			ans=max(ans,dp[i].num);
		}
		printf("%d\n",ans);
	}
}