题目:
https://www.luogu.org/problem/show?pid=1041
这是个好题;
我的第一个想法是贪心+dfs,每次切断子树最大的点的连边,一遍就可以更新;
但这个贪心是错误的,不过noip的数据良(hen)心(shui),可以拿90分;
例如下图:
能看就行,hh
根据这个贪心我们会切2,但这样,感染人数是4,(1,3,6,7);
但如果切3,感染人数是2(1,2);
所以可以爆搜,一层层地搜;
由于树的高度不确定,
若树是一条链,O(1)就可以解决;
若树非常”饱满”,每一层我们需要切一个,树高约为logn,一共切logn次,大约O(n^2)左右?我胡诌的
我太弱了,不会估算复杂度,如果哪位大佬知道,烦请@我,谢谢;
ps:
其实还有种写法叫做随机化搜索,由于数据小,所以完全可以过掉,但貌似很不靠谱,所以不写了; 其实我不会写
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=1001;
int deep[MAXN],fst[MAXN],nxt[MAXN],fa[MAXN],siz[MAXN],cnt[MAXN],rt[MAXN][MAXN];
int n,m,tot,ss,ans=21487485;
bool vis[MAXN];
struct hh
{
int from,to,cost;
}ma[MAXN];
void build(int f,int t)
{
tot++;
ma[tot]=(hh){f,t};
nxt[tot]=fst[f];
fst[f]=tot;
return;
}
void dfs_init(int x,int f)//预处理;
{
deep[x]=deep[f]+1,siz[x]=1,fa[x]=f;
ss=max(ss,deep[x]);
rt[deep[x]][++rt[deep[x]][0]]=x;
for(int i=fst[x];i;i=nxt[i])
{
int v=ma[i].to;
if(v==f) continue;
dfs_init(v,x);
siz[x]+=siz[v];
}
return;
}
void cut(int x,bool flag)//去or加标记;
{
vis[x]=flag;
for(int i=fst[x];i;i=nxt[i])
{
int v=ma[i].to;
if(v==fa[x]) continue;
cut(v,flag);
}
return;
}
void dfs(int depth,int sum,int geli)//深度,感染人数,隔离人数;
{
int num=0;
if(depth==ss+1 || geli+sum==n)//注意结束条件,对于链的情况,仅有第一个判断是不够的;
{
ans=min(ans,sum);
return;
}
for(int i=1;i<=rt[depth][0];i++)
{
int v=rt[depth][i];
if(vis[v]) num++;
}
for(int i=1;i<=rt[depth][0];i++)
{
int v=rt[depth][i];
if(!vis[v])
{
cut(v,1);
dfs(depth+1,sum+rt[depth][0]-num-1,geli+siz[v]);
cut(v,0);
}
}
return;
}
void solve()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
build(x,y),build(y,x);
}
dfs_init(1,0);
dfs(2,1,0);//从第二层点开始搜,因为我们将连边映射到了点上,即从第一层的边开始搜;
cout<<ans;
}
int main()
{
solve();
return 0;
}