【BZOJ4405】【WC2016】挑战NPC(带花树)
【BZOJ4405】【WC2016】挑战NPC(带花树)
题面
BZOJ
洛谷
Uoj
Description
小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目:
有n个球,用整数1到n编号。还有m个筐子,用整数1到m编号。
每个筐子最多能装3个球。
每个球只能放进特定的筐子中。具体有e个条件,第i个条件用两个整数vi和ui描述,表示编号为vi的球可以放进编号为ui的筐子中。
每个球都必须放进一个筐子中。如果一个筐子内有不超过1个球,那么我们称这样的筐子为半空的。
求半空的筐子最多有多少个,以及在最优方案中,每个球分别放在哪个筐子中。
小N看到题目后瞬间没了思路,站在旁边看热闹的小I嘿嘿一笑:“水题!”
然后三言两语道出了一个多项式算法。
小N瞬间就惊呆了,三秒钟后他回过神来一拍桌子:
“不对!这个问题显然是NP完全问题,你算法肯定有错!”
小I浅笑:“所以,等我领图灵奖吧!”
小O只会出题不会做题,所以找到了你——请你对这个问题进行探究,并写一个程序解决此题。
Input
第一行包含1个正整数T,表示有T组数据。
对于每组数据,第一行包含3个正整数n,m,e,表示球的个数,筐子的个数和条件的个数。
接下来e行,每行包含2个整数vi,ui,表示编号为vi的球可以放进编号为ui的筐子。
Output
对于每组数据,先输出一行,包含一个整数,表示半空的筐子最多有多少个。
Sample Input
1
4 3 6
1 1
2 1
2 2
3 2
3 3
4 3
Sample Output
2
HINT
对于所有数据,T≤5,1≤n≤3m。保证 1≤vi≤n,1≤ui≤m,且不会出现重复的条件。
保证至少有一种合法方案,使得每个球都放进了筐子,且每个筐子内球的个数不超过 3。
M<=100
题解
考虑一下可以放的球数和对答案的贡献:
3个球:1
2个球:1
1个球:0
0个球:0
我们发现就是可以放的球数整除\(2\)的结果
所以,把一个篮子拆成三个
一个球还是一个球
如果一个球可以放进一个篮子里,证明着球可以与篮子拆分出来的任意一个点匹配
现在要求的相当于篮子自身能够匹配的最大数目
如果超过了两个空,那么就可以自身与自身匹配了,从而产生贡献一。
所以考虑如下构图:
每个篮子是三个点,点与点之间互相连边
每个球是一个点,可以向它可以放的篮子拆出来的三个点连边。
这样的话,因为保证所有球都有匹配,
所以最大匹配数=球数+有贡献的篮子自身的匹配
所以最后的答案就是最大匹配数-球数。
至于如何计算方案,一定优秀增广球,再增广篮子,否则会出现篮子自身优先匹配,然后球没有匹配的情况,到时方案错误(虽然答案也是对的。。。)
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MAX 1111 inline int read() {RG int x=0,t=1;RG char ch=getchar();while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();if(ch=='-')t=-1,ch=getchar();while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();return x*t; } struct Line{int v,next;}e[MAX*MAX]; int h[MAX],cnt=1; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;} queue<int> Q; int dfn[MAX],tim; int match[MAX],pre[MAX]; int f[MAX],vis[MAX],n,m,E; int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);} int lca(int u,int v) {++tim;u=getf(u);v=getf(v);while(dfn[u]!=tim){dfn[u]=tim;u=getf(pre[match[u]]);if(v)swap(u,v);}return u; } void Blossom(int x,int y,int w) {while(getf(x)!=w){pre[x]=y,y=match[x];if(vis[y]==2)vis[y]=1,Q.push(y);if(getf(x)==x)f[x]=w;if(getf(y)==y)f[y]=w;x=pre[y];} } bool Aug(int S) {for(int i=1;i<=n+m+m+m;++i)f[i]=i,vis[i]=pre[i]=0;while(!Q.empty())Q.pop();Q.push(S);vis[S]=1;while(!Q.empty()){int u=Q.front();Q.pop();for(int i=h[u];i;i=e[i].next){int v=e[i].v;if(getf(u)==getf(v)||vis[v]==2)continue;if(!vis[v]){vis[v]=2;pre[v]=u;if(!match[v]){for(int x=v,lst;x;x=lst)lst=match[pre[x]],match[x]=pre[x],match[pre[x]]=x;return true;}vis[match[v]]=1,Q.push(match[v]);}else{int w=lca(u,v);Blossom(u,v,w);Blossom(v,u,w);}}}return false; } void init() {memset(h,0,sizeof(h));cnt=1;memset(dfn,0,sizeof(dfn));tim=0;memset(match,0,sizeof(match)); } int main() {int T=read();while(T--){n=read();m=read();E=read();init();int ans=0;for(int i=1;i<=m;++i){Add(i,i+m);Add(i+m,i);Add(i,i+m+m);Add(i+m+m,i);Add(i+m,i+m+m);Add(i+m+m,i+m);}for(int i=1;i<=E;++i){int v=read(),u=read();Add(v+m+m+m,u);Add(u,v+m+m+m);Add(v+m+m+m,u+m);Add(u+m,v+m+m+m);Add(v+m+m+m,u+m+m);Add(u+m+m,v+m+m+m);}for(int i=m+m+m+n;i;--i)if(!match[i]&&Aug(i))++ans;printf("%d\n",ans-n);for(int i=1;i<=n;++i)printf("%d ",(match[i+m+m+m]-1)%m+1);puts("");}return 0; }转载于:https://www.cnblogs.com/cjyyb/p/8719429.html
总结
以上是生活随笔为你收集整理的【BZOJ4405】【WC2016】挑战NPC(带花树)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Cookie与 Session使用详解
- 下一篇: P1514 引水入城