POJ 1018 Communication System(贪心+优化)

题目描述:有n种仪器,第i种仪器有m_i个供应商,不同供应商提供的同一种仪器有不同的带宽(B)和价格(p), n种仪器总带宽为所有仪器带宽最小值。要求从每种仪器的供应商中选一个,使得最终总带宽除以总价格最小。

限制:n<=100, m_i<=100

思路:网上说这是一个DP。如果给出了带宽的范围则可以用DP做,但是题目中并没有给出上界。我的做法是从所有仪器中选出一个,用它的带宽作为所有仪器的瓶颈,然后从带宽更大的仪器中每种仪器各选一种,使得总价格最小。显然这时只需要选出每种仪器中带宽比当前瓶颈更大且价格最低的一个。

所以可以对所有仪器进行排序(NlogN, N=10^4=sum_i=1^n(m_i)),带宽小者先,相同带宽低价者先。然后维护一个n元的下标数组index[],index[i]表示第i种仪器中带宽比当前的瓶颈大而价格最低的下标。每次计算 带宽/总价,直接对index[]所指n个仪器求和。之后对其中一个index[i]更新。求和时间为n*N=10^6, 更新index[]时间为n*N,因为每一个index[i]扫过N个元素。总复杂度为10^6级别

代码如下:

/*
	PROG: POJ1018
	PROB: Greedy
*/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <vector>
#include <algorithm>
using namespace std;

#define DEBUG 1
#define LOG(...) do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while(0)

typedef pair<int, int> ii;
typedef vector<ii> vii;
struct Dev {
	int b, p, id;
	Dev(int b, int p, int id)
	: b(b), p(p), id(id) {}
	bool operator<(const Dev& rhs) const {
		return b < rhs.b || (b==rhs.b&&p<rhs.p);
	}
};

int main(void) {
	int Z; scanf("%d", &Z);
	while (Z--) {
		int n; scanf("%d", &n);
		vector<Dev> dev;
		vector<int> index(n, -1);
		for (int i = 0; i < n; ++i) {
			int m; scanf("%d", &m);
			for (int j = 0; j < m; ++j) {
				int b, p; scanf("%d%d", &b, &p);
				dev.push_back(Dev(b, p, i));
			}
		}
		sort(dev.begin(), dev.end());
		int uninit = n;
		for (int i = 0; i < dev.size(); ++i) {
			int id = dev[i].id;
			if (index[id]!=-1) continue;
			index[id] = i, --uninit;
			if (!uninit) break;
		}
		double ans = 0.0;
		for (int i = 0; i < dev.size(); ++i) {
			int B = -1, P = 0;
			for (int j = 0; j < n; ++j) {
				P += dev[index[j]].p;
				if (B<0||B>dev[index[j]].b)
					B = dev[index[j]].b;
			}
			if (((double)B)/P>ans) ans = (double)B/P;
			int id = dev[i].id;
			int k = index[id]+1;
			bool found = false;
			while (k < dev.size()) {
				if (dev[k].id==id) {
					found = true; index[id] = k; break;
				}
				++k;
			}
			if (!found) break;
		}
		printf("%.3f\n", ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值