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

本文介绍了一种算法问题,即在一群人排队回家并提供关于自己团队前后人数信息的情况下,如何找出最大可能说真话的人数。通过将问题转化为求不相交合法区间数,并使用动态规划解决。

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

分析:

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

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

这道题画出示意图很关键,数轴表示法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);
	}
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值