POJ1848--Tree ——树形dp
题意:给你一个树,问你最少连几条边可以让树中的每一个节点在且只在一个环内。如果无法完成就输出-1。
我们设dp[i][0]为根节点为i的树变成每一个节点都在且只在一个环里所需要的最小边数。dp[i][1]为除了根节点i外其他点都在且只在一个环里所需要的最小边数。
dp[i][2]为除了根节点和一个子节点(以及子节点可有可无的链)都在且只在一个环里所需要的最小边数。
这样我们的状态转移需要考虑以下4种情况:
(第一张图最上面应该是dp[i][1] 当时写错了不好改)
具体代码如下:
#include<iostream> #include<vector> using namespace std; #define ll long long const ll inf=100000000; ll dp[105][3]; vector<int> a[105]; void solve(int u,int f) { int i,j; if(a[u].size()==1&&a[u][0]==f) { dp[u][0]=inf; dp[u][1]=0; dp[u][2]=inf; // return; } for(i=0;i<a[u].size();i++) { if(a[u][i]==f) continue; solve(a[u][i],u); } ll sum=0; for(i=0;i<a[u].size();i++) { if(a[u][i]==f) continue; sum+=dp[a[u][i]][0]; } dp[u][1]=min(dp[u][1],sum); for(i=0;i<a[u].size();i++) { if(a[u][i]==f) continue; ll now=sum-dp[a[u][i]][0]+min(dp[a[u][i]][1],dp[a[u][i]][2]); ll now1=sum-dp[a[u][i]][0]+dp[a[u][i]][2]+1; dp[u][2]=min(dp[u][2],now); dp[u][0]=min(dp[u][0],now1); } for(i=0;i<a[u].size()-1;i++) for(j=i+1;j<a[u].size();j++) { if(a[u][i]==f||a[u][j]==f) continue; ll now=1+sum-dp[a[u][i]][0]-dp[a[u][j]][0]+min(dp[a[u][i]][1],dp[a[u][i]][2])+min(dp[a[u][j]][1],dp[a[u][j]][2]); //cout<<u<<i<<j<<" "<<min(dp[a[u][i]][1],dp[a[u][i]][2])<<" "<<min(dp[a[u][j]][1],dp[a[u][j]][2])<<endl; dp[u][0]=min(dp[u][0],now); } } int main() { int i,n,x,j,y; cin>>n; for(i=0;i<n-1;i++) { cin>>x>>y; a[x].push_back(y); a[y].push_back(x); } for(i=1;i<=n;i++) for(j=0;j<=2;j++) dp[i][j]=inf; solve(1,-1); ll ans=inf; ans=dp[1][0]; if(ans==inf) cout<<"-1"<<endl; else cout<<ans<<endl; //for(i=1;i<=n;i++) cout<<dp[i][0]<<" "<<dp[i][1]<<" "<<dp[i][2]<<endl; }