題目鏈接
自己寫的狀態轉移方程總是少了點什麼,所以折騰了一個多小時也寫不出。。。。。
所以還是按紫書上思路寫的。
dp(u,0):u是服務器,u的子節點可以是服務器也可以不是。有:dp(u,0)=sum{min(dp(v,0),dp(v,1))}+1;
dp(u,1):u不是服務器,但u的父節點是服務器,所以u的子節點都不是服務器,
有: dp(u,1)=sum{dp(v,2)};
dp(u,2):當前子節點爲服務器其他子節點不爲服務器的最小值,
有:dp(u,2)=min(dp(u,1)-d(v,2)+d(v,0));
其實只要狀態轉移方程搞定了就能寫出來。
先將無根圖轉化爲有根圖,再根據狀態轉移方程進行DP,就行了。
我用了BFS進行建樹,看了例程後發現用DFS建樹還是要好一點。
具體實現見代碼:
#include<bits/stdc++.h>
#define pa pair<int,int>
using namespace std;
const int maxn=10000+10;
vector<int> edg[maxn]; ///保存邊
vector<int> son[maxn]; ///用來保存樹的結點
bool vis[maxn];
int d[maxn][3]; ///用d數組記錄狀態,減少計算量
void build_tree(){ ///轉化樹
queue<int> que;
que.push(1);
while(!que.empty()){
int a=que.front(),b;
que.pop();
if(vis[a]) continue;
else vis[a]=1;
for(int i=0;i<edg[a].size();i++){
b=edg[a][i];
if(vis[b]) continue;
else{
son[a].push_back(b);
que.push(b);
}
}
}
}
int dp(int u, int c){
if(son[u].empty()) return d[u][c]=(c==1)?0:1;
///如果u是服務器返回1否則返回0,並同時給d[u][c]賦值
else if(d[u][c]!=-1) return d[u][c];
int b,sum=0,a=INT_MAX;
for(int i=0;i<son[u].size();i++){
b=son[u][i];
if(c==0) sum+=min(dp(b,0),dp(b,1)); ///三個狀態的轉移
else if(c==1) sum+=dp(b,2);
else a=min(dp(u,1)-dp(b,2)+dp(b,0),a);
}
if(c==0) sum++; ///如果u是服務器,sum++;
return d[u][c]=(c==2)?a:sum;
}
int main(){
int n,a,b;
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++){
edg[i].clear();
son[i].clear();
}
memset(vis,0,sizeof(vis));
memset(d,-1,sizeof(d));
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
edg[a].push_back(b);
edg[b].push_back(a);
}
build_tree();
printf("%d\n", min(dp(1,0),dp(1,2))); ///根節點只會有兩種狀態。
scanf("%d",&a);
if(a==0) continue;
else break;
}
return 0;
}