poj2054 Color a Tree

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;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值