洛谷P1041 传染病控制
题目背景
近来,一种新的传染病肆虐全球。蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延。不幸的是,由于人们尚未完全认识这种传染病,难以准确判别病毒携带者,更没有研制出疫苗以保护易感人群。于是,蓬莱国的疾病控制中心决定采取切断传播途径的方法控制疾病传播。经过 WHO(世界卫生组织)以及全球各国科研部门的努力,这种新兴传染病的传播途径和控制方法已经研究清楚,剩下的任务就是由你协助蓬莱国疾控中心制定一个有效的控制办法。
题目描述
研究表明,这种传染病的传播具有两种很特殊的性质;
第一是它的传播途径是树型的,一个人X只可能被某个特定的人Y感染,只要Y不得病,或者是XY之间的传播途径被切断,则X就不会得病。
第二是,这种疾病的传播有周期性,在一个疾病传播周期之内,传染病将只会感染一代患者,而不会再传播给下一代。
这些性质大大减轻了蓬莱国疾病防控的压力,并且他们已经得到了国内部分易感人群的潜在传播途径图(一棵树)。但是,麻烦还没有结束。由于蓬莱国疾控中心人手不够,同时也缺乏强大的技术,以致他们在一个疾病传播周期内,只能设法切断一条传播途径,而没有被控制的传播途径就会引起更多的易感人群被感染(也就是与当前已经被感染的人有传播途径相连,且连接途径没有被切断的人群)。当不可能有健康人被感染时,疾病就中止传播。所以,蓬莱国疾控中心要制定出一个切断传播途径的顺序,以使尽量少的人被感染。
你的程序要针对给定的树,找出合适的切断顺序。
输入输出格式
输入格式:
输入格式的第一行是两个整数n(1≤n≤300)和p。接下来p行,每一行有两个整数i和j,表示节点i和j间有边相连(意即,第i人和第j人之间有传播途径相连)。其中节点1是已经被感染的患者。
输出格式:
只有一行,输出总共被感染的人数。
输入输出样例
输入样例#1:
7 6 1 2 1 3 2 4 2 5 3 6 3 7
输出样例#1:
20分 贪心
100分 搜索
3
/* 观察题目,发现好像需要切断的父亲儿子关系在最优情况下,应该是父亲在满足出度最大的前提下子树节点最多,儿子应该也是在满足出度最大的前提下子树节点最多的那个点,所以开了好几个堆贪心了一下,代码特别暴力,至于多么暴力,我都T到20分了 */ #include<iostream> #include<cstdio> #include<queue> #define maxn 310 using namespace std; int du[maxn],num,head[maxn],sz[maxn],n,m,cnt,que[maxn]; bool vis[maxn]; struct node{ int s,d,id; bool operator < (const node x)const{ if(d!=x.d)return d<x.d; return s<x.s; } }; priority_queue<node>q; priority_queue<node>q1[maxn]; struct Node{int to,pre;}e[maxn]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } void dfs(int now,int father){ sz[now]=1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==father)continue; dfs(to,now); sz[now]+=sz[to]; } } node make_node(int x,int y,int z){ node res;res.s=x;res.d=y;res.id=z; return res; } bool end(){ for(int i=1;i<=n;i++) if(!q1[i].empty())return 0; return 1; } int main(){ freopen("Cola.txt","r",stdin); scanf("%d%d",&n,&m); int x,y; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); Insert(x,y),Insert(y,x); du[x]++;du[y]++; } du[1]++; dfs(1,0); q.push(make_node(sz[1],du[1],1)); for(int i=head[1];i;i=e[i].pre){ int to=e[i].to; q1[1].push(make_node(sz[to],du[to],to)); } //while(!q1[1].empty()){cout<<q1[1].top().id<<' ';q1[1].pop();}cout<<endl; cnt=1;que[cnt]=1; vis[1]=1; while(1){ if(end())break; int now=q.top().id;q.pop(); int nowson=q1[now].top().id;q1[now].pop();//切断连边,踢出联通块 sz[now]-=sz[nowson];du[now]--; vis[nowson]=1; //cout<<now<<' '<<nowson<<endl; q.push(make_node(sz[now],du[now],now)); int w=cnt; for(int i=1;i<=w;i++){ int point=que[i]; for(int j=head[point];j;j=e[j].pre){ int to=e[j].to; if(vis[to])continue; for(int k=head[to];k;k=e[k].pre){ int toto=e[k].to; if(vis[toto])continue; q1[to].push(make_node(sz[toto],du[toto],toto)); } q.push(make_node(sz[to],du[to],to)); cnt++; } } } printf("%d",cnt); return 0; }
/* 切断一个结点上面的那条路就能把这条结点以及子树上的结点全部保护 所以我们可以求出最多能保护多少个结点 然后用sum数组存储每个结点的子树和 m为最深的层数 然后还有保存每一层的结点是哪些 f[i]表示i结点上面的那条边是不是被切断了 然后把根节点1作为第一层 从第二层开始搜索 切断第i个结点就把f[i]改为true 增加一个神似并茶几的find找爹函数 如果它到树根的路上有f为true的点 说明当前状态下它已经被保护了,就跳过它 然后没有被保护的结点就保护住再搜索下一层 */ #include<iostream> #include<cstdio> #include<vector> #define maxn 501 using namespace std; vector<int>a[maxn]; int sz[maxn],dep[maxn],m,n,fa[maxn],p,map[maxn][maxn],mx; bool f[maxn]; int num,head[maxn]; struct node{int to,pre;}e[maxn*2]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } bool find(int x){ if(x==1)return false; if(f[x])return true; return find(fa[x]); } void build(int now,int father){//求同一深度的点集,每个子树的大小 dep[now]=dep[father]+1; fa[now]=father; a[dep[now]].push_back(now); m=max(m,dep[now]); sz[now]=1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==father)continue; build(to,now); sz[now]+=sz[to]; } } void dfs(int depth,int ans){ if(depth==m+1)return; int n1=a[depth].size(),x; for(int i=0;i<n1;i++){ x=a[depth][i]; if(find(x))continue; f[x]=1; mx=max(mx,ans+sz[x]); dfs(depth+1,ans+sz[x]); f[x]=0; } } int main(){ scanf("%d%d",&n,&p); int x,y; for(int i=1;i<=p;i++){ scanf("%d%d",&x,&y); Insert(x,y),Insert(y,x); } build(1,1); dfs(2,0); printf("%d",n-mx); }