【bzoj1907】最小树上路径覆盖(贪心||DP)
一开始第一想法居然是网络流orz,发现是树就不放这种大杀器了。
BZOJ1907非权限
题意:RT。
DP;f[x]表示这个点以下子树完全覆盖,x作为链端最小路径和。g[x]表示这个点以下子树完全覆盖,x不作为链端,即作为拐点的最小路径和。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int T,n,cnt; int last[10005],v[10005]; bool mark[10005]; struct edge{int to,next;}e[20005]; void insert(int u,int v) { e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt; e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt; } void dfs(int x,int f) { v[x]=1; int tot=0; for(int i=last[x];i;i=e[i].next) { if(e[i].to==f)continue; dfs(e[i].to,x); v[x]+=v[e[i].to]; if(!mark[e[i].to])tot++; } if(tot>=2)v[x]-=2,mark[x]=1; else if(tot==1)v[x]--; } int main() { T=read(); while(T--) { cnt=0; memset(last,0,sizeof(last)); memset(v,0,sizeof(v)); memset(mark,0,sizeof(mark)); n=read(); for(int i=1;i<n;i++) { int a=read(),b=read(); insert(a,b); } dfs(1,0); printf("%d\n",v[1]); } return 0; }DP写完到网上看题解发现一大串的贪心orz,想了想也是,贪心,能作拐点肯定就做拐点就可以了。
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> using namespace std; const int maxn = 20005; int f[maxn]; bool mark[maxn]; int en[maxn],nt[maxn],owo,la[maxn]; void addedge(int a,int b) { en[++owo] = b; nt[owo] = la[a]; la[a] = owo; } int t,n; void dfs(int x,int ba) { int tot = 0; f[x] = 1; mark[x] = 0; for(int it=la[x];it;it=nt[it]) { if(en[it]==ba) continue; dfs(en[it],x); f[x] += f[en[it]]; if(!mark[en[it]]) ++tot; } if(tot>=2) { mark[x] = 1; f[x]-=2; } else if(tot>=1) f[x]-=1; } void solve() { scanf("%d",&n); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } dfs(1,0); printf("%d\n",f[1]); owo=0; for(int i=1;i<=n;i++) la[i] = 0; } int main() { int t; scanf("%d",&t); while(t--) solve(); }