题目链接
http://codeforces.com/contest/260/problem/D
题目大意
给你一棵树(注意不要出现环)的所有节点, 节点分为黑、白两类,只有不同颜色的节点才能相连, 每个节点还有一个值, 该值表示该点所连边的权值之和, 最后让你输出每条边所连的两个节点以及其权值(题目为特判)
思路
该题几乎没有用到算法,但十分考验思维
首先sum(edge) = sum(node)
由于只有不同颜色的node才能相连, 所以sum(white) = sum(black)
由这两个式子我们可以得出这么一个结论:从一个节点出发, 取另一种颜色的另一个节点, 我们把较小的值作为该边的权值,那么较大的就要减去这个权值, 再用已经减过的值继续递归地找另一种颜色的节点, 这样一定可以组成一张符合题意的树, 如果还有节点没有使用,那么该节点的值一定为0, 否则就无法加入树中
有很多题解都是排序后贪心找, 其实不用排序, 直接dfs就好了
代码
#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 5;
struct node
{
int id, val;
bool operator < (node & x)
{
return val < x.val;
}
};
queue<node>b;
queue<node>w;
struct edg
{
int l, r, val;
};
vector<edg>ans;
void dfs(node v, bool c)
{
if(c)
{
while(!b.empty())
{
node tmb = b.front();
b.pop();
edg tmp;
if(tmb.val >= v.val)
{
tmp.l = v.id;
tmp.r = tmb.id;
tmp.val = v.val;
ans.push_back(tmp);
tmb.val -= v.val;
dfs(tmb, 0);
return ;
}
tmp.l = tmb.id;
tmp.r = v.id;
tmp.val = tmb.val;
ans.push_back(tmp);
v.val -= tmb.val;
}
}
else
{
while(!w.empty())
{
node tmw = w.front();
w.pop();
edg tmp;
if(tmw.val > v.val)
{
tmp.l = v.id;
tmp.r = tmw.id;
tmp.val = v.val;
ans.push_back(tmp);
tmw.val -= v.val;
dfs(tmw, 1);
return ;
}
tmp.l = tmw.id;
tmp.r = v.id;
tmp.val = tmw.val;
ans.push_back(tmp);
v.val -= tmw.val;
}
}
}
int main()
{
int n;
scanf("%d", &n);
int tb = 0, tw = 0;
for(int i=1; i<=n; ++i)
{
int c, v;
scanf("%d%d", &c, &v);
if(c)
{
node tmp;
tmp.id = i;
tmp.val = v;
b.push(tmp);
}
else
{
node tmp;
tmp.id = i;
tmp.val = v;
w.push(tmp);
}
}
int rw = w.front().id, rb = b.front().id;
node temp = b.front();
b.pop();
dfs(temp, 0);
while(w.size()) //注意最后可能还有节点没有用完,这些没用完的节点的值一定是0,否则那两个式子就不成立了
{
node tmw = w.front();
w.pop();
edg tmp;
tmp.l = tmw.id;
tmp.r = rb;
tmp.val = 0;
ans.push_back(tmp);
}
while(b.size())
{
node tmb = b.front();
b.pop();
edg tmp;
tmp.l = tmb.id;
tmp.r = rw;
tmp.val = 0;
ans.push_back(tmp);
}
int len = ans.size();
for(int i=0; i<len; ++i)
{
printf("%d %d %d\n", ans[i].l, ans[i].r, ans[i].val);
}
return 0;
}