「UVA 10859」題解
阿新 • • 發佈:2021-08-17
Description
Link
給定一棵樹,選其中的一些點使每條邊都與至少一個所選點相鄰。
在滿足上述條件的情況下,使得被兩個點同時相鄰的邊最多。
其中 \(1\leq n\leq10^3\)。
Solution
經典的換根 DP。
如果只考慮要求 1,設 \(dp[i][0]\) 表示 i 選不選(0/1)時照亮所有 i 的子節點所需要的最少燈數。
則:
- \(dp[i][0]=\sum_{j=1}^{son[i]}dp[j][1]\)
- \(dp[i][1]=\sum_{j=1}^{son[i]}\min(dp[j][0],dp[j][1])\)
i 選,那麼子節點可選可不選,i能全部照亮。
i 不選,那麼每條邊都只能子節點來照亮,所以所有子節點必選。
設 \(f[i][0]\)
如果 i 不選,依然所有的子節點都必選。
- \(f[i][0]=\sum_{j=1}^{son[i]}f[j][1]\)
否則,檢視 \(dp[j][0]\) 和 \(dp[j][1]\) 的值,判斷 \(dp[i][0]\) 由哪一個值得來,那麼 \(f[i][0]\) 也由對應的 \(f[j][0/1]\) 得來。
最後統計答案時按滿足條件 1 的最優情況判斷即可。
具體可見程式碼。
Code:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int MAXN=1e3+5; int t,n,m,head[MAXN<<1],cnt,dp[MAXN][2],pd[MAXN][2]; bool vis[MAXN]; struct ren{ int next,to; }a[MAXN<<1]; void add(int x,int y) { a[++cnt].to=y; a[cnt].next=head[x]; head[x]=cnt; } void dfs(int now,int fa) { vis[now]=1; dp[now][1]=1,dp[now][0]=0; for(int i=head[now];i;i=a[i].next) { int v=a[i].to; if(v==fa) { continue; } dfs(v,now); dp[now][1]+=min(dp[v][1],dp[v][0]); dp[now][0]+=dp[v][1]; } } void DP(int now,int fa){ pd[now][0]=pd[now][1]=0;//pd陣列即為上文的f陣列 for(int i=head[now];i;i=a[i].next) { int v=a[i].to; if(v==fa) { continue; } DP(v,now); pd[now][0]+=pd[v][1]; if(dp[v][0]<dp[v][1]) { pd[now][1]+=pd[v][0]; continue; } if(dp[v][1]<dp[v][0]) { pd[now][1]+=pd[v][1]+1; continue; } pd[now][1]+=max(pd[v][0],pd[v][1]+1); } } int main() { scanf("%d",&t); while(t--) { int ans=0,tot=0; memset(vis,0,sizeof(vis)); memset(head,0,sizeof(head)); cnt=0; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); x++; y++; add(x,y); add(y,x); } for(int i=1;i<=n;i++) { if(!vis[i]) { dfs(i,0); ans+=min(dp[i][0],dp[i][1]); DP(i,0); if(dp[i][0]<dp[i][1]) { tot+=pd[i][0]; continue; } if(dp[i][1]<dp[i][0]) { tot+=pd[i][1]; continue; } tot+=max(pd[i][1],pd[i][0]); } } printf("%d %d %d\n",ans,tot,m-tot); } return 0; }
\(\Bbb{End.}\)
\(\Bbb{Thanks}\) \(\Bbb{For}\) \(\Bbb{Reading.}\)