题目链接
自己写的状态转移方程总是少了点什么,所以折腾了一个多小时也写不出。。。。。
所以还是按紫书上思路写的。
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;
}