bzoj 1369: [Baltic2003]Gem
1369: [Baltic2003]Gem
Time Limit: 2 Sec Memory Limit: 64 MBDescription
给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数 唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小。
Input
先给出一个数字N,代表树上有N个点,N<=10000 下面N-1行,代表两个点相连
Output
最小的总权值
Sample Input
10
7 5
1 2
1 7
8 9
4 1
9 7
5 6
10 2
9 3
7 5
1 2
1 7
8 9
4 1
9 7
5 6
10 2
9 3
Sample Output
14
HINT
Source
WJMZBMR:
这题首先是不能用奇偶层染色的办法来做的,我构造出了至少需要1-3的反例,同时用数学归纳法可以证明对于任意n,都有树不能用1-n达到最优解(提示:使用大量叶子节点逼迫某节点染2。。)。。
但是我的构造法弄出来的反例的大小至少是指数增长的,所以我感觉对于N<=10000,10种颜色足够了。。更精确的测试表明3种就OK了(!!!!!我可以构造出1000个以内的需要4种颜色的反例啊囧。。这个数据好弱啊。。)。。然后就是简单的树形DP。。。比赛的时候很明显直接DP就可以了。。证明不重要(*^__^*) 嘻嘻……
dp[i][j]表示以i为根的子树,将i染为j种颜色的最小权值和;
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 1000008 using namespace std; int n,m,tot,head[MAXN],vet[MAXN],next[MAXN],dp[MAXN][6]; inline int read(){ char ch=getchar(); int f=1,x=0; while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } void add(int x,int y){ tot++; next[tot]=head[x]; head[x]=tot; vet[tot]=y; } void dfs(int u,int fa){ for(int i=head[u];i;i=next[i]){ int y=vet[i]; if(y==fa) continue; dfs(y,u); for(int j=1;j<=5;j++){ int res=2000000000; for(int k=1;k<=5;k++) if(j!=k) res=min(res,dp[y][k]); dp[u][j]+=res; } } for(int i=1;i<=5;i++) dp[u][i]+=i; } int main(){ n=read(); for(int i=1;i<n;i++){ int x,y; x=read(); y=read(); add(x,y); add(y,x); } dfs(1,-1); int ans=2000000000; for(int i=1;i<=5;i++) ans=min(ans,dp[1][i]); printf("%d",ans); }