51Nod_1475 建设国家【贪心+枚举】

                                           51Nod_1475 建设国家

                                      http://www.51nod.com/Challenge/Problem.html#!#problemId=1475

 

 

题目

小C现在想建设一个国家。这个国家中有一个首都,然后有若干个中间站,还有若干个城市。现在小C想把国家建造成这样的形状:选若干(可以是0个)的中间站把他们连成一条直线,然后把首都(首都也是一个中间站)连在这一条直线的左端。然后每个点可以连一个城市,特别的是最右端的点可以连接两个城市。现在有n个城市的规划供小C选择。但是,他们那儿的交通条件比较差,他们那儿一天是2*H个小时,每个城市里面的人每天都会去首都拿一样东西,从他们所在的城市出发,到了首都之后拿了东西就走(拿东西的时间可以忽略不计),他们要在2*H个小时之内返回他们自己的家中(从家中出发到返回家中不超过2*H小时)。每个城市有两个属性,一个是城市的直径,另外一个是能居住的人口数目。对于第i个城市而言,这两个属性分别是hi,pi。城市的直径的意思是离这个城市出口最远的人想要出城先要在城里行走的最少的时间。在首都,中间站,城市之间行走要花费1小时的时间。小C想选择一些城市然后通过若干的中间站和首都连接起来,在每个人能在2*H小时返回的条件下所有城市居住的总人口数目要最多。样例解释:最上面的蓝点表示首都,其它的蓝点表示中间站,剩下的红圈表示选择的城市。

输入

单组测试数据。第一行包含两个整数n 和H (1 ≤ n ≤ 1000,1 ≤ H ≤ 1000000000),表示可供选择的城市数目和时间限制。
接下来n行,每行有两个整数hi, pi (1 ≤ hi ≤ H, 1 ≤ pi ≤ 1000),第i个城市的两个属性,即直径和能容纳人口数。

输出

输出最多能居住的人口数目。

样例输入

5 10
1 1
1 1
2 2
3 3
4 4

样例输出

11

分析

那么问题就变为将城市放置在相应的方框中,使得城市直径+对应方框中的数<=H,且总人口最大。该如何放置城市呢?设某一城市的直径为h,那么H-h表示从城市出口到首都所允许的最大的距离,那么只要方框中的数<=H-h,那么这个城市就可以放置在这个方框中。贪心法,先对城市进行排序,以城市人口为第一关键字,降序排列,以城市直径为第二关键字,升序排列,然后依次放置城市,放置城市的策略是找到能放置此城市的最后的一层,即H-h层,如果H-h层已经放置了,就继续往序号小的层查找。看到这,发现这个贪心问题和 51Nod_1163 最高的奖励【贪心】差不多,但此处的不同是最后一层有两个放置点,我们可以先枚举最后一层的位置,然后问题就和“最高的奖励”一样了。具体看程序

C++程序

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1005;

typedef long long ll;

struct Node{
	int h,p;
	bool operator < (const Node &t)const
	{
		return p==t.p?(h<t.h):(p>t.p);//p为第一关键字,降序排列,h为第二关键字,升序排列 
	}
}a[N];

int num[N];//记录到首都的距离为i的方框的个数 

int main()
{
	int n,H;
	scanf("%d%d",&n,&H);
	int Max=-1;//统计最多需要多少层 
	for(int i=0;i<n;i++)
	{
		scanf("%d%d",&a[i].h,&a[i].p);
		if(a[i].h<H)
		{
			int temp=min(H-a[i].h,n-1);//当n大于等于2时,最多只需要n-1层。 
			Max=max(temp,Max);
		}
	} 
	if(Max<1)
	  Max=n;//n==1的特殊情况 
	//排序
	sort(a,a+n);
	ll ans=0;
	for(int i=1;i<=Max;i++)//枚举最后一层的位置 
	{
		for(int j=1;j<i;j++)
		  num[j]=1;
		num[i]=2;//最后一层有两个位置
		//计算这种情况下的最大值
		ll temp=0;
		for(int k=0;k<n;k++)
		{
			if(a[k].h>=H)//当城市的直径大于等于H时,这个城市就不能被选择
			  continue;
			int t=min(H-a[k].h,i);
			for(;t>0;t--)
			{
				if(num[t]>0)
				{
					num[t]--;
					temp+=a[k].p;
					break;
				}
			}
		} 
		ans=max(ans,temp);
	}
	printf("%lld\n",ans);
	return 0;
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值