BZOJ4405: [wc2016]挑战NPC

Posted mt-li

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
 
 
跟着AKCqhzdy大佬学了一发带花树(一般图匹配),然后找了一道题练练手
woc这道题真tm神
首先讲做法:把每一个筐拆成三角形,然后把球和能到的筐的三边连起来,跑一边带花树,然后用ans-n
ans是匹配数
再来说说正确性,因为每个球都能匹配啊,然后ans必定大于等于n
然后它还会让筐来匹配,如果一个筐能够匹配,就说明这个筐最多被用了一个
然后用最大匹配保证正确性
woc太强了Orz出题人
 
代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

int n,m;
struct node
{
    int x,y,next;
}a[210000];int len,last[11000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}

int fa[11000];
int findfa(int x)
{
    if(fa[x]==x)return x;
    fa[x]=findfa(fa[x]);return fa[x];
}
void unit(int x,int y)
{
    int fx=findfa(x),fy=findfa(y);
    if(fx!=fy)fa[fx]=fy;
}

int match[11000],pre[11000],mark[11000];
int head,tail,list[11000];
int ts,vis[11000];
int LCA(int x,int y)
{
    ts++;
    while(x!=0)
    {
        x=findfa(x);
        vis[x]=ts;
        x=pre[match[x]];
    }
    while(y!=0)
    {
        y=findfa(y);if(vis[y]==ts)return y;
        vis[y]=ts;
        y=pre[match[y]];
    }
}
void group(int x,int r)
{
    while(x!=r)
    {
        int y=match[x],z=pre[y];
        if(findfa(z)!=r)pre[z]=y;
        if(mark[y]==2) list[++tail]=y, mark[y]=1;
        if(mark[z]==2) list[++tail]=z, mark[z]=1;
        unit(x,y);unit(y,z);
        x=z;
    }
}
bool findniu(int s)
{
    for(int i=1;i<=n+3*m;i++)fa[i]=i;
    memset(pre,0,sizeof(pre));
    memset(mark,0,sizeof(mark));
    head=1;tail=1;list[1]=s;mark[s]=1;
    while(head<=tail)
    {
        int x=list[head];head++;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(match[x]==y)continue;
            if(findfa(x)==findfa(y))continue;
            if(mark[y]==2)continue;
            
            if(mark[y]==0)
            {
                if(match[y]==0)
                {
                    int p1=y,p2=x;
                    while(p1!=0&&p2!=0)
                    {
                        int tt=match[p2];
                        match[p1]=p2;match[p2]=p1;
                        p1=tt;p2=pre[p1];
                    }
                    return true;
                }
                else
                {
                    pre[y]=x;
                    list[++tail]=match[y];
                    mark[match[y]]=1;
                    mark[y]=2;
                }
            }
            else if(mark[y]==1)
            {
                int r=LCA(x,y);
                if(findfa(x)!=r)pre[x]=y;
                if(findfa(y)!=r)pre[y]=x;
                group(x,r);group(y,r);
            }
        }
    }
    return false;
}

int main()
{
    int T_T;
    scanf("%d",&T_T);
    while(T_T--)
    {
        int E,x,y;
        scanf("%d%d%d",&n,&m,&E);
        //1~n ball n+1~n+m*3 base 
        len=0;memset(last,0,sizeof(last));
        for(int i=1;i<=m;i++)
        {
            ins(n+(i-1)*3+1,n+(i-1)*3+2);
            ins(n+(i-1)*3+2,n+(i-1)*3+1);
            ins(n+(i-1)*3+2,n+(i-1)*3+3);
            ins(n+(i-1)*3+3,n+(i-1)*3+2);
            ins(n+(i-1)*3+3,n+(i-1)*3+1);
            ins(n+(i-1)*3+1,n+(i-1)*3+3);
        }
        while(E--)
        {
            scanf("%d%d",&x,&y);
            ins(x,n+(y-1)*3+1);ins(n+(y-1)*3+1,x);
            ins(x,n+(y-1)*3+2);ins(n+(y-1)*3+2,x);
            ins(x,n+(y-1)*3+3);ins(n+(y-1)*3+3,x);
        }
        
        int ans=0;
        ts=0;memset(vis,0,sizeof(vis));
        memset(match,0,sizeof(match));
        for(int i=1;i<=n+3*m;i++)
            if(match[i]==0)
                if(findniu(i)==true)ans++;
        printf("%d\n",ans-n);
    }
    return 0;
}

by_lmy

以上是关于BZOJ4405: [wc2016]挑战NPC的主要内容,如果未能解决你的问题,请参考以下文章

[WC2016]挑战NPC 解题报告

[WC2016]挑战NPC

P4258 [WC2016]挑战NPC(一般图匹配)

[WC2016]挑战NPC

[WC2016]挑战NPC

[WC2016]挑战NPC