• bzoj4405 [wc2016]挑战NPC


    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

    正解:带花树算法。

    学了带花树,现在还有点不理解,然后就把这道题作为入门题。。

    一道很神奇的一般图匹配问题,不过暴力分挺多的。。

    暴力分不多说了:爆搜+模拟构造+二分图匹配+拆点二分图匹配就有$60$分。。

    然后就讲正解了:

    把每个筐子拆成$3$个筐子,然后两两之间连边。

    对于所有的条件,球向筐子的$3$个点连边。

    然后跑一般图最大匹配,答案就是匹配数-$n$。

    为什么呢?

    我们可以注意到,如果一个筐子没有装球,那么匹配数是$1$。

    如果一个筐子装了$1$个球,那么匹配数是$2$。

    如果一个筐子装了$2$个球,那么匹配数是$2$。

    如果一个筐子装了$3$个球,那么匹配数是$3$。

    然后我们发现,这样对应起来,答案就是匹配数-$n$。

      1 //It is made by wfj_2048~
      2 #include <algorithm>
      3 #include <iostream>
      4 #include <cstring>
      5 #include <cstdlib>
      6 #include <cstdio>
      7 #include <vector>
      8 #include <cmath>
      9 #include <queue>
     10 #include <stack>
     11 #include <map>
     12 #include <set>
     13 #define inf (1<<30)
     14 #define M (1000010)
     15 #define N (1010)
     16 #define il inline
     17 #define RG register
     18 #define ll long long
     19 #define id(i,j) (3*(i-1)+j)
     20 
     21 using namespace std;
     22 
     23 struct edge{ int nt,to; }g[M];
     24 
     25 int head[N],match[N],pre[N],vis[N],id[N],fa[N],q[M],h,t,n,m,e,mn,num,cnt,ans;
     26 
     27 il int gi(){
     28     RG int x=0,q=1; RG char ch=getchar();
     29     while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
     30     if (ch=='-') q=-1,ch=getchar();
     31     while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
     32     return q*x;
     33 }
     34 
     35 il void insert(RG int from,RG int to){
     36     g[++num]=(edge){head[from],to},head[from]=num; return;
     37 }
     38 
     39 il int find(RG int x){ return fa[x]==x ? x : fa[x]=find(fa[x]); }
     40 
     41 il int lca(RG int x,RG int y){
     42     ++cnt;
     43     for (x=find(x),y=find(y);vis[x]!=cnt;swap(x,y))
     44     if (x) vis[x]=cnt,x=find(pre[match[x]]);
     45     return x;
     46 }
     47 
     48 il void change(RG int x,RG int y,RG int k){
     49     while (find(x)!=k){
     50     pre[x]=y; RG int z=match[x];
     51     if (id[z]==1) id[z]=0,q[++t]=z;
     52     if (find(z)==z) fa[z]=k;
     53     if (find(x)==x) fa[x]=k;
     54     y=z,x=pre[y];
     55     }
     56     return;
     57 }
     58 
     59 il int bfs(RG int ini){
     60     for (RG int i=1;i<=mn;++i) id[i]=-1,fa[i]=i;
     61     h=t=0,q[++t]=ini,id[ini]=0;
     62     while (h<t){
     63     RG int x=q[++h],v;
     64     for (RG int i=head[x];i;i=g[i].nt){
     65         v=g[i].to;
     66         if (id[v]==-1){
     67         pre[v]=x,id[v]=1;
     68         if (!match[v]){
     69             RG int now=v,t,la;
     70             while (now){
     71             t=pre[now],la=match[t];
     72             match[t]=now,match[now]=t,now=la;
     73             }
     74             return 1;
     75         }
     76         id[match[v]]=0,q[++t]=match[v];
     77         } else if (!id[v] && find(x)!=find(v)){
     78         RG int g=lca(x,v);
     79         change(x,v,g),change(v,x,g);
     80         }
     81     }
     82     }
     83     return 0;
     84 }
     85 
     86 il void work(){
     87     n=gi(),m=gi(),e=gi(),num=0;
     88     memset(head,0,sizeof(head));
     89     for (RG int i=1,u,v;i<=e;++i){
     90     u=gi(),v=gi(),u+=3*m;
     91     insert(u,id(v,1)),insert(id(v,1),u);
     92     insert(u,id(v,2)),insert(id(v,2),u);
     93     insert(u,id(v,3)),insert(id(v,3),u);
     94     }
     95     for (RG int i=1;i<=m;++i){
     96     insert(id(i,1),id(i,2)),insert(id(i,2),id(i,1));
     97     insert(id(i,1),id(i,3)),insert(id(i,3),id(i,1));
     98     insert(id(i,3),id(i,2)),insert(id(i,2),id(i,3));
     99     }
    100     mn=3*m+n,cnt=ans=0,memset(match,0,sizeof(match));
    101     memset(pre,0,sizeof(pre)),memset(vis,0,sizeof(vis));
    102     for (RG int i=mn;i;--i) if (!match[i]) ans+=bfs(i); printf("%d
    ",ans-n);
    103     for (RG int i=3*m+1;i<=mn;++i) printf("%d ",(match[i]-1)/3+1); return;
    104 }
    105 
    106 int main(){
    107 #ifndef ONLINE_JUDGE
    108     freopen("npc.in","r",stdin);
    109     freopen("npc.out","w",stdout);
    110 #endif
    111     RG int T=gi();
    112     while (T--) work();
    113     return 0;
    114 }
  • 相关阅读:
    Docker Private Registry
    Dockerfile
    docker存储卷
    392. 判断子序列
    1576. 替换所有的问号
    270. 最接近的二叉搜索树值
    292. Nim 游戏
    680. 验证回文字符串 Ⅱ
    876. 链表的中间结点
    543. 二叉树的直径
  • 原文地址:https://www.cnblogs.com/wfj2048/p/7218920.html
Copyright © 2020-2023  润新知