您的位置:首页 > 其它

FZU - 2219 StarCraft

2016-04-04 17:54 274 查看

1.题面

http://acm.fzu.edu.cn/problem.php?pid=2219

2.题意

你有m个工人,要造n个建筑,每个工人只能建造一个建筑,每个建筑只能被一个工人造。但是你有一项技能,你可以选择一个工人,把他变成一个蛋,这样k个时间单位后,这个蛋就会孵化出两个工人。

现在给出建造第i栋建筑需要的时间t[i],求建造完这所有n栋建筑需要的时间。

3.思路

这道题中每一种建造方案都可以用图来表示,图中有m棵二叉树,表示m个初始工人,每一次分叉都是使用魔法将一个工人变成两个工人,因为每次需要k单位的时间,所以我们将每条边的权值设置为k,分叉节点的权值设为0。然后如果这个工人不再进行分裂,而是去做建造第i个建筑,它的下方就直接接一个权值为t[i]的节点。

为了说明的方便,我们引入一个虚的根节点,从根节点往这m棵树的树根每个都引一条权值为0的边。

那么我们的问题就变成了,找出一种方案,使所有叶子节点到虚的根节点中的权值和的最大值最小。

在这样一棵树中,所有的叶子节点都代表一个建设建筑物的操作。

可以证明,如果一个(距离根节点较远的叶子节点)的权值大于(一个距离根节点距离较小的节点),我们交换这两个节点之后结果必然不会更坏,而且有可能会更好。

根据以上性质,我们可以推测出,在一棵最优的树中权值最小的两个节点,距离根节点的距离也必然是最大的。至于为什么是两个呢,因为权值最小的节点必然有兄弟啊。

这个样子,我们就找到了最底端的两个节点,同时也解决了这个问题。因为在找到了最底端的两个节点之后,我们可以将它们删除,同时将它们的父亲节点的权值设置为max(LeftChild,RightChild)+k。我们的问题就变成了如何是剩下的节点最优,这是一个已经解决的问题。

4.代码

/*****************************************************************
> File Name: tmp.cpp
> Author: Uncle_Sugar
> Mail: uncle_sugar@qq.com
> Created Time: 2016年04月01日 星期五 20时31分43秒
*****************************************************************/
# include <cstdio>
# include <cstring>
# include <cmath>
# include <cstdlib>
# include <climits>
# include <iostream>
# include <iomanip>
# include <set>
# include <map>
# include <vector>
# include <stack>
# include <queue>
# include <algorithm>
using namespace std;

const int debug = 1;
const int size  = 5000 + 10;
const int INF = INT_MAX>>1;
typedef long long ll;

int main()
{
std::ios::sync_with_stdio(false);cin.tie(0);
int i,j;
int T;
cin >> T;
priority_queue<int,vector<int>,greater<int> > pri_que;
while (T--){
while (!pri_que.empty()) pri_que.pop();
int n,m,k,tmp;
cin >> n >> m >> k;
for (i=0;i<n;i++){
cin >> tmp;
pri_que.push(tmp);
}
while (pri_que.size()>m){
pri_que.pop();
tmp = pri_que.top();
pri_que.pop();
pri_que.push(tmp+k);
}
while (pri_que.size()>1)
pri_que.pop();
cout << pri_que.top() << '\n';
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: