1018 Communication System【枚举+贪心】:论贪心的正确性

题目大意

需要n个设备,第i个设备有mi个厂商,每个厂商做该设备有两个参数,带宽和价格,每个设备从其中一个厂商处购得,现在定义B为n个设备中带宽的最小值,P为n个设备的总价值,求B/P的最大值

思路分析

rank终于到破5000了,感动。不过这道题也是蛮波折的还是自己太菜了 。没想到可以这样贪心,再知道了题解之后仍然思考了挺久的关于该算法的正确性。也给自己提个醒,忽视了枚举的作用。

思路:从小到大枚举带宽,对于第i个设备,在mi个厂商中选择带宽不小于当前枚举值且价格最小的那个,正确性在于求B/P的最大值,我们就要让B尽量大,让P尽量小,因为我们在枚举B的时候取的是大于等于B的带宽,所以当前的分子B就是枚举值,然后再找满足带宽大小约束的价格尽量小的加起来就是分母P,因此对于每一个带宽我们都有一个最优解,最后取全局最优即可。

唯一令人欣慰的是知道题解一遍过了/(ㄒoㄒ)/~~

//memory:364K time:32ms
#include<iostream>
#include<string>
#include<string.h>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;

#define MAX 105
#define ll int
#define inf 0x3ffffff

struct E {
	ll b, p;
	E(ll x = 0, ll y = 0) { b = x, p = y; }
};

bool cmp(E e1, E e2) {
	return e1.b > e2.b;//按照带宽排序
}

vector< E > a[MAX];

int main() {
	ll t, n, m, x, y; scanf("%d", &t);
	vector<ll> v;
	for (int b = 0; b < t; b++) {
		scanf("%d", &n);
		for (int i = 0; i < n; i++)a[i].clear();

		for (int i = 0; i < n; i++) {
			scanf("%d", &m);
			for (int j = 0; j < m; j++) {
				scanf("%d%d", &x, &y);
				a[i].push_back(E(x, y));
				v.push_back(x);//把所有的价格集中起来
			}
			sort(a[i].begin(), a[i].end(), cmp);//根据带宽从大到小排序
		}
		sort(v.begin(), v.end());
		ll sign = 1;//如果有一个设备没有任何带宽大于v[i]的选择 那么就退出循环
		double res = 0;
		for (int i = 0; i < v.size() && sign; i++) {
			if (i == 0 || v[i] != v[i - 1]) {//去重,找到不重复的带宽
				ll B = inf, P = 0, tb, tp;
				for (int j = 0; j < n&&sign; j++) {//遍历所有的element
					if (a[j][0].b < v[i]) { sign = 0; break; }//最大的B都小于v[i],退出
					tb = 0, tp = inf;
					for (int k = 0; k < a[j].size() && sign; k++) {
						if (a[j][k].b < v[i])break; //之后的带宽都没有v[i]大
						if (a[j][k].p < tp) {
							tp = a[j][k].p, tb = a[j][k].b;//选b大于v[i]的最小price
						}
					}
					B = min(B, tb), P += tp;
				}
				if (sign)res = max(res, 1.0*B / P);
			}
		}
		printf("%.3f\n", res);
	}
}
发布了269 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/csyifanZhang/article/details/105600818
今日推荐