4 0:(1) 1 1:(2) 2 3 2:(0) 3:(0) 5 3:(3) 1 4 2 1:(1) 0 2:(0) 0:(0) 4:(0)
1 2
———————————————————————分割线—————————————————————————
题目分析:
用最少的士兵将所有的道路都监控起来
这题是最小顶点覆盖,可以用二分匹配,但是今天学树形DP,走起~~
基础模板:
1、建图,用邻接表
2、dfs,遍历整个图,直到叶子,然后子问题一层一层往上合并成整个问题
额,都是废话~~~
状态转移 dp[i][0] i节点不放士兵所需要的最少方案数,dp[i][1] i节点放士兵的最少方案数
每个节点都有两个状态,放士兵或者不放士兵
1.如果该节点不放士兵,那么它的所有子节点必须放
2.如果该节点放士兵,那么它的子节点有两种决策,放或不放,去最小的
code^*^:
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #define maxn 1510 using namespace std; struct node { int v,next; }edge[maxn*maxn]; int e; int head[maxn]; int dp[maxn][2]; int vis[maxn]; void dfs(int u) { vis[u]=1; dp[u][1]=1; for(int e=head[u];e!=-1;e=edge[e].next){ int v=edge[e].v; if(vis[v]) continue; dfs(v); //dfs到最底层 dp[u][1]+=min(dp[v][1],dp[v][0]); //解决子问题 dp[u][0]+=dp[v][1]; } } void addedge(int u,int v) //邻接表建图 { edge[e].v=v; edge[e].next=head[u]; head[u]=e++; edge[e].v=u; edge[e].next=head[v]; head[v]=e++; } int main() { int n; while(scanf("%d",&n)!=EOF){ memset(head,-1,sizeof head); memset(dp,0,sizeof dp); memset(vis,0,sizeof vis); int x,y,v,s=-1; e=0; while(n--){ scanf("%d:(%d)",&x,&y); while(y--){ scanf("%d",&v); addedge(x,v); } if(s==-1&&y) s=x; } dfs(s); printf("%d\n",dp[s][0]>dp[s][1]?dp[s][1]:dp[s][0]); //去最小 } return 0; }
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<vector> #define maxn 1510 using namespace std; vector<int> g[maxn]; int dp[maxn][2]; int vis[maxn]; void dfs(int u) { vis[u]=1; dp[u][1]=1; for(int i=0;i<g[u].size();i++){ int v=g[u][i]; if(vis[v]) continue; dfs(v); dp[u][1]+=min(dp[v][1],dp[v][0]); dp[u][0]+=dp[v][1]; } } int main() { int n; while(scanf("%d",&n)!=EOF){ memset(dp,0,sizeof dp); memset(vis,0,sizeof vis); int x,y,v,s=-1; for(int i=0;i<maxn;i++){ g[i].clear(); } while(n--){ scanf("%d:(%d)",&x,&y); while(y--){ scanf("%d",&v); g[x].push_back(v); g[v].push_back(x); } if(s==-1&&y) s=x; } dfs(s); printf("%d\n",dp[s][0]>dp[s][1]?dp[s][1]:dp[s][0]); } return 0; }