【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();
}

浙公网安备 33010602011771号