l链接:http://poj.org/problem?id=2054
这个题挺吃力。我先转载一下网上的思路。
道题就是要求 Sigma( i * Ci ) (i = 1 .. n) 的值最小,{ Ci } 是节点费用的一个排列,同时要满足父节点要出现在子节点前
面。如果没有父节点出现在子节点前面这个限制,那么答案很明显。当{ Ci }按降序排列的时候,Sigma的值是最小的。当有这个限
制的时候情况也是类似的。考虑某一个可行解,就是{ Ci }的某一个排列。找到其中的最大值,比如为Ck,它有一个父节点比如Cp。显然Cp要出现在Ck之前。更进一步,Cp就应该出现在Ck的前一个位置。只有 这样才有可能Sigma的值最小。不然我们可以将Ck位置向前移动,得到一个更小的Sigma值,并且不破坏上面的约束。既然Cp就出现在Ck的前一个位 置,那么它们其实就是连在一起的,可以最为一个整体来看。这样问题的规模就有n减小到n-1。然后重复这一过程,直到所有的位置都确定下来。
算法流程:
1. 令所有节点S值均为1,每个节点生成序列中仅有一个元素,即为它本身。
2. 若树中只剩一个结点,则输出这个这个结点的生成序列。
3. 取出Ci/Si值最大的非根结点Max。
4. 将Max和其父亲合并,新合并出的结点Union的各个参数为:Cunion=CMax+CPa(max),SUnion=SMax+SPa(Max),同时Union
的生成序列为Pa(Max)的生成序列与Max的生成序列连接而成。
代码基本上也是照抄的,现在也没完全理解。等以后再来看看吧。
#include<iostream>
#include<cstring>
#define MAXN 1010
using namespace std;
struct Tree
{
int ci,fi,ans;
int vist,father;
};
Tree tre[MAXN];
struct Cnum
{
int cc,num;
};
int find(int i)
{
if(i!=tre[i].father&&tre[tre[i].father].vist)
tre[i].father=find(tre[i].father);
return tre[i].father;
}
int main()
{
int n,r;
int a,b;
int i,j;
while(cin>>n>>r&&n&&r)
{
memset(tre,0,sizeof(tre));
for(i=1;i<=n;i++)
{
cin>>tre[i].ci;
tre[i].ans=tre[i].ci;
tre[i].fi=1;
}
for(i=0;i<n-1;i++)
{
cin>>a>>b;
tre[b].father=a;
}
tre[r].father=r;
for(i=1;i<n;i++)
{
double maxc=0;
int p;
for(j=1;j<=n;j++)
{
if(!tre[j].vist&&maxc<tre[j].ci*1.0/tre[j].fi&&j!=r)
{
maxc=tre[j].ci*1.0/tre[j].fi;
p=j;
}
}
tre[p].vist=1;
int f=find(p);
tre[f].ans+=tre[p].ans+tre[p].ci*tre[f].fi;
tre[f].fi+=tre[p].fi;
tre[f].ci+=tre[p].ci;
}
cout<<tre[r].ans<<endl;
}
return 0;
}