树形DP
老实说一开始是考虑用点的个数减去最大独立集的点的个数,但是很快就发现不对,数据如下:
6
1 2
1 3
1 4
1 5
2 6
在这个例子中我们很显然的发现节点2有两台服务器与之相连,是不符合题意的
。。。然后就不会做了。
定义状态dp[i][k]其中k表示该点的状态,k=0表示该点是一个服务器,k=1表示该点并不是一个服务器,但是其父亲是一个服务器,k=2表示该点和其父亲都不是服务器
这三种情况对应着三个状态转移方程
k=0的时候,对于节点i的子节点j,j既可以是服务器,也可以不是服务器,因此方程为dp[i][0]=sum(min(dp[j][0],dp[j][1]))+1 所求为服务器数量,要记得+上自己
k=1的时候,因为其父亲是一个服务器,这就要求每一个子节点都必须不是服务器,不然该点就会有两个服务器与之相连,不合题意,因此方程是dp[i][1]=sum(dp[j][2])
k=2的时候,因为其和其父亲都不是服务器,这就要求i的某一个子节点必须是服务器,而且其余的子节点必须不是服务器,因此有方程dp[i][2]=dp[j][0]+Σdp[L][2] 其中L代表i的儿子且L!=j,我们需要枚举j,对于每一次枚举j,我们都需要把那个Σ算出来,需要m次,m为儿子个数,同时枚举j也需要m次,一共需要m^2次。但是我们可以对这个式子变一下形来降低时间复杂度
Σdp[L][2]=(Σdp[j'][2])-dp[j][2]这里的j'的取值范围是i的所有儿子,然后我们就可以发现,(Σdp[j'][2])这一项实际上就是dp[i][1],因此,新的状态转移方程就是
dp[i][2]=min(dp[i][1]-dp[j][2]+dp[j][0])
然后还有需要注意一点的就是,要注意溢出的情况,因为dp[i][2]是一个min表达式,我们通常会给dp[i][2]赋一个比较大初值,但是考虑这一组数据
100
1 2
2 3
2 4
2 5
.....
2 100
我们会发现,最优解就是在2点放一个服务器,但假若我们的dfs一开始是在1处放置了服务器,那么2是不放的,这就要求2的每一个子节点都必须不放服务器,2的每一个子节点都不会再有子节点了。。假若你的比较大的初值赋得不是很好的话,按照k=1的转移方程,dp[2][1]=sum(dp[j][2]),溢出了
我的解决办法是,因为数据上限是10000,我的初值就是10010,比上限大一点,而且保证最坏情况下也达不到这个10010,当然了,你也可以给dp[2][1]特判,假若dp[2][1]一旦大于你的INF,又重新设置为INF
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxm=1E4+10;
int n,ind,dp[maxm][3],x,y,dfs(int i,int k,int fa);
vector<int> v[maxm];
int main(){
ios_base::sync_with_stdio(0);
while(cin>>n){
memset(dp+1,-1,sizeof(dp[0])*n);
for(int i=1;i<n;++i){
cin>>x>>y;
v[x].push_back(y);
v[y].push_back(x);
}
for(ind=1;v[ind].size()!=1;++ind);
cout<<min(dfs(ind,0,0),dfs(ind,2,0))<<endl;
for(int i=1;i<=n;++i)
v[i].clear();
cin>>n;
if(n==-1)
break;
}
return 0;
}
int dfs(int i,int k,int fa){
if(dp[i][k]!=-1)
return dp[i][k];
dp[i][k]=0;
if(k==1){
for(int j=0;j<v[i].size();++j)
if(v[i][j]!=fa)
dp[i][k]+=dfs(v[i][j],2,i);
}else if(k==2){
dp[i][k]=maxm;
for(int j=0;j<v[i].size();++j)
if(v[i][j]!=fa)
dp[i][k]=min(dp[i][k],dfs(i,1,fa)-dfs(v[i][j],2,i)+dfs(v[i][j],0,i));
}else{
for(int j=0;j<v[i].size();++j)
if(v[i][j]!=fa)
dp[i][k]+=min(dfs(v[i][j],0,i),dfs(v[i][j],1,i));
++dp[i][k];
}
return dp[i][k];
}