洛谷 P3155 [CQOI2009]葉子的染色 解題報告
阿新 • • 發佈:2018-06-03
spa AI 成功 整數 表示 輸入格式 scan 簡單 max
P3155 [CQOI2009]葉子的染色
題目描述
給一棵m個結點的無根樹,你可以選擇一個度數大於1的結點作為根,然後給一些結點(根、內部結點和葉子均可)著以黑色或白色。你的著色方案應該保證根結點到每個葉子的簡單路徑上都至少包含一個有色結點(哪怕是這個葉子本身)。 對於每個葉結點u,定義c[u]為從根結點從U的簡單路徑上最後一個有色結點的顏色。給出每個c[u]的值,設計著色方案,使得著色結點的個數盡量少。
輸入輸出格式
輸入格式:
第一行包含兩個正整數m, n,其中n是葉子的個數,m是結點總數。結點編號為1,2,...,m,其中編號1,2,... ,n是葉子。以下n行每行一個0或1的整數(0表示黑色,1表示白色),依次為c[1],c[2],...,c[n]。以下m-1行每行兩個整數a,b(1<=a < b <= m),表示結點a和b 有邊相連。
輸出格式:
僅一個數,即著色結點數的最小值。
顯然是樹形DP。
\(dp[i][j]\)表示以\(i\)為子樹外面(註意是“外面”,不是這個點)需染\(0,1,2\)(即不需要)的最小染色點數
轉移:
\(dp[i][0]=\sum min(dp[son][0],dp[son][2]);\)
\(dp[i][1]=\sum min(dp[son][1],dp[son][2]);\)
\(dp[i][2]=\sum dp[son][2];\)
當然有更簡單的轉移方法。
然後開始推換根。
成功沒推出來
事實上這個題都給了提示,“可以選擇一個”,根只要不是度為1的節點都是可以的。
證明:兩個相鄰的點不可能換根後更優,分6種情況討論即可。
code:
#include <cstdio> #include <cstring> int max(int x,int y) {return x>y?x:y;} int min(int x,int y) {return x<y?x:y;} const int N=10010; int m,n; int dp[N][3];//以i為子樹外面需染0,1,2(即不需要)的最小染色點數 struct Edge { int to,next; }edge[N*2]; int head[N],cnt=0,used[N]; void add(int u,int v) { edge[++cnt].to=v;edge[cnt].next=head[u];head[u]=cnt; } void dfs(int now) { used[now]=1; int sum0=0,sum1=0,sum2=0; for(int i=head[now];i;i=edge[i].next) { int v=edge[i].to; if(!used[v]) { dfs(v); sum0+=min(dp[v][0],dp[v][2]); sum1+=min(dp[v][1],dp[v][2]); sum2+=dp[v][2]; } } if(now>n) { dp[now][0]=sum0; dp[now][1]=sum1; dp[now][2]=min(min(sum0,sum1)+1,sum2); } } int ans=0x3f3f3f3f; int color[N]; int main() { scanf("%d%d",&m,&n); int u,v; for(int i=1;i<=n;i++) scanf("%d",color+i); for(int i=1;i<m;i++) { scanf("%d%d",&u,&v); add(u,v);add(v,u); } memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) { dp[i][color[i]]=0; dp[i][2]=1; } dfs(n+1); printf("%d\n",dp[n+1][2]); return 0; }
2018.6.2
洛谷 P3155 [CQOI2009]葉子的染色 解題報告