UOJ171&bzoj4405 【WC2016】挑战NPC

81 篇文章 0 订阅
49 篇文章 0 订阅

http://www.elijahqi.win/2018/01/29/uoj171bzoj4405/

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
uoj的题目还需要输出一个方案 但是输出方案这里显然我就sb了 调了 很久 发现是思想有误
如果说上来就瞎jb暴力 其实有60分 然而我这个辣鸡..当模拟的时候只搞到50 全场最菜 垫底..
前面的一部分分直接暴力搜索可搞 然后n*m的 贪心放 首先每个都放一个 然后能放的就尽量都集中放起来
剩下的部分分就跑两遍网络流 每个点向每个筐连1 每个筐向汇连1 然后网络流跑一遍验证即可 如果不满足条件说明答案是0 那么就重建图 把筐向汇点连的边改为3 输出即可

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
struct node{
    int y,next,z;
}data[33000],edge[33000];
int q[15][25],ans,ans1[25],num,h[440],n,m,e,level[440],T,st;
inline void dfs(int x,int as1){
    if(x==n+1){
        if (as1<=ans) return;ans=as1;
        for (int i=1;i<=m;++i)
            for (int j=1;j<=q[i][0];++j)ans1[q[i][j]]=i;
        return;
    }if (as1<ans) return;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (q[y][0]==3) continue;bool flag=0;
        if (q[y][0]==1) --as1,flag=1;
        ++q[y][0];q[y][q[y][0]]=x;dfs(x+1,as1);--q[y][0];if (flag) ++as1;
    }
}
inline void insert1(int x,int y,int z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;edge[num]=data[num];
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;edge[num]=data[num];
}
inline bool bfs(){
    memset(level,0,sizeof(level));level[0]=1;queue<int>q;q.push(0);
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            if (level[y]||!z) continue;level[y]=level[x]+1;q.push(y);if (y==T) return 1;
        }
    }return 0;
}
inline int dfs1(int x,int s){
    if (x==T) return s;int ss=s;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if (level[x]+1==level[y]&&z){
            int xx=dfs1(y,min(z,s));if(!xx) level[y]=0;
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;if (!s) return ss;
        } 
    }return ss-s;
}
int main(){
    freopen("uoj171.in","r",stdin);
    int TT=read();
    while(TT--){
        n=read();m=read();e=read();
        if (e==n*m){
            for (int i=1;i<=e;++i) read(),read();
            if (n<=m){
                printf("%d\n",m);
                for (int i=1;i<=n;++i) printf("%d ",i);puts("");continue;
            }ans=m-((n-m+1)/2);printf("%d\n",ans);
            if (n>m){
                int now=1;
                for (int i=1;i<=m;++i) printf("%d ",i);
                for (int i=1;i<=n-m;++i){
                    printf("%d ",now);
                    if ((i&1)==0)++now;
                }
            }continue;
        }
        if (m<=10&&n<=20&&e<=25){
            num=0;memset(h,0,sizeof(h));ans=-1;
            for (int i=1;i<=e;++i){
                int x=read(),y=read();data[++num].y=y;data[num].next=h[x];h[x]=num;
            }dfs(1,m);printf("%d\n",ans);
            for (int i=1;i<=n;++i) printf("%d ",ans1[i]);puts("");  continue;
        }num=1;memset(h,0,sizeof(h));
        for (int i=1;i<=e;++i){
            int x=read(),y=read()+n;
            insert1(x,y,1);
        }T=n+m+1;
        for (int i=1;i<=n;++i) insert1(0,i,1);st=num+1;
        for (int i=1;i<=m;++i) insert1(i+n,T,1);int ff=0;
        while(bfs()) ff+=dfs1(0,inf);
        if (ff==n){
            printf("%d\n",m);
            for (int i=1;i<=n;++i){
                for (int j=h[i];j;j=data[j].next){
                    int y=data[j].y,z=data[j].z;if (y<=n+m&&y>=n+1&&!z) printf("%d ",y-n);
                }
            }puts("");
            continue;
        }
        for (int i=2;i<st;++i) data[i]=edge[i];
        for (int i=st;i<=num;++i) if (i&1) data[i].z=0;else data[i].z=3;
        puts("0");ff=0;while(bfs()) ff+=dfs1(0,inf);
        for (int i=1;i<=n;++i){
            for (int j=h[i];j;j=data[j].next){
                int y=data[j].y,z=data[j].z;if (y<=n+m&&y>=n+1&&!z) printf("%d ",y-n);
            }
        }puts("");
    }
    return 0;
}

正解:带花树+思路
首先把每个筐拆成三个点 然后给其中的两个点连上边 然后我 这个物品分别向他给出的条件的那三个点都连一下 可以yy一下 三个点都连起来和只给两条边连起来的效果是一样的因为我只需要满足这样一种关系即可 即:如果没有球向他连,自己匹配贡献1 如果有球向他连接 球的匹配+自己匹配-球*1 正好也满足条件 (这时候如果是三条边就比较好想 然后 再往上依此类推即可
最后输出的时候方案数就是匹配数-n
uoj还要输出方案数这时候就需要注意一些了 我必须要首先去匹配物品和框之间的边 然后再匹配框内互相匹配的情况 否则输出方案不对

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 660
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct node{
    int y,next;
}data[N*N<<1];
int num,n,m,e,nm,pre[N],h[N],fa[N],col[N],match[N],visit[N],tim,q[N],l,r;
inline void insert1(int x,int y){
    data[++num].y=y;data[num].next=h[x];h[x]=num;
    data[++num].y=x;data[num].next=h[y];h[y]=num;
}
inline int find(int x){while(fa[x]!=x) x=fa[x]=fa[fa[x]];return x;}
inline int lca(int x,int y){
    x=find(x);y=find(y);++tim;
    for (;;swap(x,y)){
        if (x){
            if (visit[x]==tim) return x;
            visit[x]=tim;
            if (match[x]) x=find(pre[match[x]]);else x=0;
        }
    }
}
inline void change(int x,int y,int f){
    while(find(x)!=f){
        pre[x]=y;y=match[x];
        if (col[y]==1) col[y]=0,q[++r]=y;
        fa[x]=fa[y]=f;x=pre[y];
    }
}
inline bool check(int x){
    memset(pre,0,sizeof(pre));memset(col,-1,sizeof(col));col[x]=0;
    l=1;r=0;q[++r]=x;for (int i=1;i<=n+nm;++i) fa[i]=i;
    while(l<=r){
        int x=q[l++];
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y;
            if (col[y]==-1){
                col[y]=1;pre[y]=x;
                if (!match[y]){
                    for (int last;x;y=last,x=pre[y])
                        last=match[x],match[y]=x,match[x]=y;return 1;
                }else q[++r]=match[y],col[match[y]]=0;
            } else if (!col[y]&&find(x)!=find(y)){
                int t=lca(x,y);change(x,y,t);change(y,x,t);
            }
        }
    }return 0;
}
int main(){
//  freopen("bzoj4405.in","r",stdin);
    int T=read();
    while(T--){
        n=read();m=read();e=read();nm=3*m;memset(h,0,sizeof(h));num=0;

        for (int i=1;i<=e;++i){int x=read()+nm,y=(read()-1)*3;insert1(x,y+1);insert1(x,y+2);insert1(x,y+3);}
        for (int i=1;i<=m;++i) insert1((i-1)*3+1,(i-1)*3+2);
        int ans=0;tim=0;
        for (int i=nm+1;i<=nm+n;++i) if (!match[i]&&check(i)) ++ans;
        for (int i=1;i<=nm;++i) if (!match[i]&&check(i)) ++ans;
        printf("%d\n",ans-n);memset(visit,0,sizeof(visit));
        for (int i=nm+1;i<=n+nm;++i) printf("%d ",(match[i]-1)/3+1);puts("");memset(match,0,sizeof(match));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值