有一个N个结点的有根树,1是这个树的根。现在要对这N个结点依次进行
染色,每个结点染色要花费1个单位的时候,同时要满足一个结点仅在其
父亲被染色后才可被染色,每个结点有个权值Ci,如果我们在第Ti时间对
i号结点染色,则付出总代价为Sigma(Ti*Ci),1<=i<=N.
现在给出这个树和每个点的权值,请构造一种染色顺序,使得总代价最小.
N<=1000
5 1//5个点
1 2 1 2 4 //5个点的权值
1 2
1 3
2 4
3 5
0 0//整个测试结束
Sample Output
33
Sol:
第1次找到5这个点,其权值为4,块大小为1,比值是最大的。
累加值为1*4..1是其父亲点3所在的块的大小。
第2次找到3这个点,其权值为5,块大小为2,比值是最大的。
累加值为1*5..1是其父亲点1所在的块的大小。
第3次找到2这个点,其权值为2,块大小为1,比值是最大的。
累加值为3*2..3是其父亲点1所在的块的大小。
第4次找到4这个点,其权值为2,块大小为1,比值是最大的。
累加值为4*2..4是其父亲点1所在的块的大小。(4的父亲开始是2,但在染2的时候,将4的父亲变成2的父亲,也就是1了。)
一个非常经典的贪心题.开始每个点i有其权值vi,大小为ti,每次找vi/ti最大的点进行染色,染色的代价为其父亲点所在块的大小。因为要先染其父亲点再染这个点。染完后将所有父亲点指i的,改为指向i的父亲点,并修改i的父亲点所在块的大小.因为数据范围太小了,所以没有用并查集了。这个题还有许多变形,后面再来补。
#include<bits/stdc++.h> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,r,x,y,fa[1010],v[1010]; double t[1010]; int main() { while(1) { scanf("%d%d",&n,&r); if(n==0&&r==0) break; int ans=0,sum; double mx; for(int i=1;i<=n;i++) { scanf("%d",&v[i]); ans+=v[i]; //假设每个点都被首先染色过 t[i]=1; } for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); fa[y]=x; } for(int i=1;i<=n;i++) { mx=sum=0; for(int j=1;j<=n;j++) if(v[j]/t[j]>mx&&j!=r) mx=v[j]/t[j],sum=j; for(int j=1;j<=n;j++) //将所有点,如果其父亲从前是sum,现在的父亲点改为fa[sum] if(fa[j]==sum) fa[j]=fa[sum]; ans+=v[sum]*t[fa[sum]]; //取出这个点的权值,及它父亲点的t值,注意是父亲点的.. //也就是说当它父亲点染过后,它才被染,所以父亲点所在连通块有多少个点,它被染的序号就是乘上多少 v[fa[sum]]+=v[sum]; t[fa[sum]]+=t[sum]; v[sum]=0; } printf("%d\n",ans); } return 0; }
https://blog.csdn.net/abc473848880/article/details/13629577
#include<queue> #include<cstdio> #include<cstring> const int maxn=1010; using namespace std; struct zy{ int num,W,T; zy(){} zy(int _num,int _W,int _T):num(_num),W(_W),T(_T){ } bool operator <(const zy &a) const { return W*a.T<a.W*T; } }; inline int getint(){ char ch=getchar(); int tmp=0; while (ch<'0' || ch>'9') ch=getchar(); for (;ch<='9' && ch>='0';ch=getchar()) tmp=tmp*10+ch-'0'; return tmp; } int tot=0; int pre[maxn],now[maxn],son[maxn]; inline void add(int a,int b){ pre[++tot]=now[a]; now[a]=tot; son[tot]=b; } bool used[maxn]; priority_queue<zy>h; int n; inline void prepare(){ tot=0; memset(now,0,sizeof(int)*n); memset(used,0,sizeof(bool)*n); while (!h.empty()) h.pop(); } int root; int tim[maxn],v[maxn]; int fa[maxn]; void init(){ n=getint()+1; prepare(); n--; root=getint(); for (int i=1;i<=n;++i){ tim[i]=1; v[i]=getint(); h.push(zy(i,v[i],1)); } int a,b; for (int i=1;i<n;++i){ a=getint(); b=getint(); add(a,b); fa[b]=a; } } inline int findmax(int root){ while (used[h.top().num] || h.top().num==root) h.pop(); return h.top().num; } inline int find(int x){ if (used[fa[x]]) fa[x]=find(fa[x]); return fa[x]; } inline void Union(int a,int b){ v[b]+=v[a]; tim[b]+=tim[a]; for (int p=now[a];p;p=pre[p]) fa[son[p]]=b; } void work(){ int ans=0; for (int i=1;i<n;++i){ int tmp=findmax(root); used[tmp]=1; int pre=find(tmp); ans+=tim[pre]*v[tmp]; Union(tmp,pre); h.push(zy(pre,v[pre],tim[pre])); } ans+=v[root]; printf("%d\n",ans); } int main() { while (1){ init(); if (!root) break; work(); } return 0; }