1475 建设国家 (经典贪心——最高利润 变式)

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

题意:

       有N个城市,每个城市两个权值,time代表在城市中行走花费的时间,value代表该城市

有多少人,现在想通过一条链式的中间点将这些城市中的某一些关联起来,链头是首都,

且规定,每个中间节点只能连接一个城市,但是中间节点中的尾巴节点可以连接两个城市,

城市和中间点之间行走要花费一小时,现给定一时间H,要求最终构建的城市网络满足任意

一个城市中的人往返首都所花费的时间<=2*H,怎样构建这个网络能使得最终城市的人数

之和最大,求出最大人数和。

思路分析:

最大利润原题:

       商店有N个商品,每个商品有一个到期时间和价值,且规定每天仅能出售一个商品,

问怎么安排出售商品的顺序能使得最终获得最大利润。

优先队列解法:

     维护一个关于商品利润的小根堆, 对所有商品按照到期时间排序,顺序遍历所有商品。

     若当前商品到期时间大于优先队列中商品数目,则该商品可以直接出售。

     否则,即当前商品到期时间 t 等于优先队列中商品的数量,即前 t 天已经安排了 t 件

商品出售,则将其与堆中最小利润的物品比较,若其利润大,则用其去替换堆顶元素,

否则,继续遍历商品。

本题:

      再看本题,如果用H-在城市中花费的时间,得到是从首都到不同城市所需要的

步数/时间。即从某时刻开始,在规定时间内必须到达某个城市,让我们从N个城市

中选择一些城市安排到规划中使得最终能获得最大利润。一样的题。

     不同之处在于本题最后一天(相对的)可以出售两件物品。

解决方案:

       即到达最后一个中间节点时特殊处理,但怎么判断当前的中间节点是否是尾节点并

不好判断,所以不妨对于每一个中间节点都当作最终的尾节点处理,在用其阶段中某个

利润较大城市替换之前城市之前,记录一下该城市连接到尾节点时的人口总和。因为随

着中间节点的增加,大利润城市不断对较小论润城市的替换,中间节点越多总利润越大。

      上述分析是错误的,即最大利润的方案并不一定在未变式的最终节点上添加点取得。

可能前几个所需时间少的城市具有很大的人口数,以至于通过增加中间节点增加的人口数

不如将某城市作为尾节点所连接城市最终贡献的人口数多。

      综上当出现”到期时间“等于队列中城市数目的时候,假设当前就是在尾节点上添加第2城市,

即求出一个答案作为候选答案之一。

     纠结问题:会不会最后添加的第二城市可以由前面被抛弃的某个城市替换从而得到

更大的价值。答案是不会,因为在替换之前已经把这种情况下的总价值求出来作为了

候选答案之一。

例如下图:

   3步的8代替了2步的7,然后3步的6想最为最终第二城市,但显然 7_{2} 和 8_{3} 的组成更优,

但这种情况在 8_{3} 替换 7_{2} 之前已经被试过并保存了临时答案。

 

 

 

代码实现:

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e5+100;
struct Node{
	int time,value;
}arr[N];
bool cmp(Node aa,Node bb){
	return aa.time<bb.time;
}
priority_queue<int ,vector<int>,greater<int> >Q;
int main() {
	int n,h,ans,maxs;
	while(cin>>n>>h){
		while(Q.size())Q.pop();
		ans=maxs=0;
		
		for(int i=1;i<=n;i++){
			cin>>arr[i].time>>arr[i].value;
			arr[i].time=h-arr[i].time;
		}	
		sort(arr+1,arr+1+n,cmp);
		for(int i=1;i<=n;i++){
			
			if(arr[i].time<=0)continue;
			if(arr[i].time>Q.size()){
				Q.push(arr[i].value);
				ans+=arr[i].value;
				
			}else {
				maxs=max(maxs,ans+arr[i].value);//作为候选答案 
				if(arr[i].value>Q.top()){
					ans-=Q.top();
					Q.pop();
					Q.push(arr[i].value);
					ans+=arr[i].value;
				}	
			}
		}
		ans=max(ans,maxs);
		cout<<ans<<endl;
	}
	return 0;
}

THE END;

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
贪心算法是一种问题求解方法,它在每一步总是做出当前情况下的最优选择,以期望获得最优解。而"最大整数"同样可以使用贪心算法来求解。 对于"最大整数"的问题,我们可以考虑如下的贪心策略:从高位开始,尽可能选择较大的数字。具体步骤如下: 1. 对于给定的整数,我们首先将其转化为一个数组,其中每个元素表示整数的一个位数。 2. 从最高位(最左侧)开始,遍历数组。 3. 对于当前位上的数字,从9开始递减,找到第一个小于等于当前数字的最大数字。 4. 如果找到了符合条件的最大数字,将其放在当前位。否则,不做任何操作。 5. 继续向下遍历,重复步骤3-4。 6. 最终,得到的数组即为满足条件的最大整数。 以一个具体的例子说明上述算法:假设给定的整数为5372。 1. 将整数转化为数组[5, 3, 7, 2]。 2. 从最高位开始遍历。 3. 对于第一位5,从9开始递减,找到第一个小于等于5的数字,为7。 4. 将7放在第一位,得到[7, 3, 7, 2]。 5. 对于第二位3,从9开始递减,找到第一个小于等于3的数字,为3(与当前数字相等)。 6. 不做任何操作,得到[7, 3, 7, 2]。 7. 对于第三位7,从9开始递减,找到第一个小于等于7的数字,为7。 8. 将7放在第三位,得到[7, 3, 7, 2]。 9. 对于第四位2,从9开始递减,找到第一个小于等于2的数字,为2。 10. 将2放在第四位,得到[7, 3, 7, 2]。 11. 遍历结束,最终得到的数组为[7, 3, 7, 2],转化为整数为7372。 通过上述贪心算法,我们得到了满足条件的最大整数7372。证明了贪心算法在"最大整数"问题中的有效性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值