UVA10984 Double NP-hard 二分匹配+二分图+奇圈

在一个无向图中找一个点集,这个点集即是最大独立集,又是最小覆盖集。

首先图中不能存在奇环,因为如果存在奇环,最大独立集一定不等于最小覆盖集,画一个圈就可以发现了

其次图可以划分为二分图,并且集合X等于集合Y的数量。否则同上,两个集合数量不相等。最大独立集大于等于最大的集合数量,最小覆盖集小于等于最小的集合数量,所以不等

最后,如果存在则一定是完美匹配。否则最大独立集一定不等于最小覆盖集(补集关系)

最后字典序最小的话,就从1开始输出同样的集合中的点即可。

奇圈的判断在二分图划分的时候同时进行就可以了。


Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu

[]   [Go Back]   [Status]  

Description

Download as PDF

Problem C
Double NP-hard
Time Limit: 2 second

"Name and Class Year:
Course to be Covered: (Course Number and Title)
Reason for covering the course independently:"
Hamilton College Application for Independent
Coverage of Course Work

Definitions
In this problem, a graph is a set of n vertices together with a set of m edges, where an edge is an unordered pair of different vertices (edges are undirected). The two vertices that comprise an edge are said to be that edge's endpoints. A vertex cover of a given graph G is a subset C of its vertices, such that each edge of G has at least one of its endpoints in C. An independent set of a given graph G is a subset S of its vertices, such that no edge of G has both of its endpoints in S.

The problem of finding a minimum vertex cover (that is, a vertex cover of the smallest possible size) for any graph is NP-hard. The problem of finding a maximum independent set of any graph is also NP-hard. That is a formal way of saying that no one knows whether there exists an algorithm that runs in time polynomial in n and solves any one of the two problems.

We want to define a class of problems that are even harder than the NP-hard problems. We are going to call them "Double NP-hard"! Your job is to solve the first Double NP-hard problem.

Problem
Given a graph G, find a subset C of its vertices that is both a minimum vertex cover and a maximum independent set.

Input
The first line of input gives the number of cases, NN test cases follow. Each one starts with two lines containing n (0<=n<=1000) and m(0<=m<=100000) as above. The next m lines will each describe an edge of G as a pair of different vertices, which are numbered from 1 to n.

Output
For each test case, output one line containing "Case #x:" followed by either "Impossible" if there is no answer or the size k of the set C. In the latter case, on the next line, print the k vertices of C in increasing order, separated by spaces. If there are multiple answers, print the lexicographically smallest one.

Sample InputSample Output
4
2
1
1 2
0
0
10
0
4
4
1 2
2 3
3 4
4 1
Case #1: 1
1
Case #2: 0

Case #3: Impossible
Case #4: 2
1 3


Problemsetter: Igor Naverniouk
Special thanks: Wladek Kwasnicki, Frank Chu


#include <iostream>
#include <cstring>
#include <cmath>
#include <climits>
#include <algorithm>
#include <cstdio>

using namespace std;

#define MAXN  1005
#define MAXM  200015

struct edges
{
    int to;
    int next;
}edge[MAXM];

int nx,ny,n,m,en;
int first[MAXN];
int cx[MAXN],cy[MAXN],distx[MAXN],disty[MAXN];
int que[MAXN],head,tail;
int ans;
int color[MAXN],cnt1,cnt2;

void init()
{
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    memset(first,-1,sizeof(first));
    en=0;ans=0;
}

void add(int a, int b)
{
    edge[en].to=b;
    edge[en].next=first[a];
    first[a]=en++;
}

bool bfs()
{
    int i,j,k;
    bool flag(0);
    int h,t;
    memset(distx,0,sizeof(distx));
    memset(disty,0,sizeof(disty));
    head=tail=0;
    for(i=1;i<=nx;i++)
        if (cx[i]==-1&&color[i]==1) que[tail++]=i;
    while(head!=tail)
    {
        for(h=head,t=tail;h!=t;h=(h+1)%MAXN)
        {
            i=que[h];
            if(color[i]!=1) continue;
            for(k=first[i];k!=-1;k=edge[k].next)
            {
                j=edge[k].to;
                if(color[j]!=-1) continue;
                if(!disty[j])
                {
                    disty[j]=distx[i]+1;
                    if (cy[j]==-1) flag=1;
                    else
                    {
                        distx[cy[j]]=disty[j]+1;
                        que[tail]=cy[j];
                        tail=(tail+1)%MAXN;
                    }
                }
            }
        }
        head=t;
    }
    return flag;
}

bool dfs(int i)
{
    int j, k;
    if(color[i]!=1) return 0;
    for(k=first[i];k!=-1;k=edge[k].next)
    {
        j=edge[k].to;
        if(color[j]!=-1) continue;
        if(disty[j]==distx[i]+1)
        {
            disty[j]=0;
            if(cy[j]==-1||dfs(cy[j]))
            {
                cx[i]=j,cy[j]=i;
                return 1;
            }
        }
    }
    return 0;
}

int Hopcroft_Karp()
{
    int i,j;
    while(bfs())
          for(i=1; i<=nx;i++)
            if(color[i]==1&&cx[i]==-1&&dfs(i)) ans++;
    return ans;
}

bool bipartite(int u,int c)
{
    color[u]=c;
    if(c==1) cnt1++;
    else cnt2++;
    for(int i=first[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(color[v]==0)
        {
            if(!bipartite(v,-c))
                return false;
        }
        else
            if(color[v]==color[u])
                return false;
    }
    return true;
}


int main()
{
    int t;
    cin>>t;
    for(int cs=1;cs<=t;cs++)
    {
        cin>>n>>m;
        init();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            cin>>u>>v;
            add(u,v);
            add(v,u);
        }
        printf("Case #%d: ",cs);
        if(n==0&&m==0)
        {
            printf("0\n\n");
            continue;
        }
        memset(color,0,sizeof(color));
        bool ok=1;
        for(int i=1;i<=n;i++)
            if(color[i]==0)
            {
                cnt1=cnt2=0;
                if(!bipartite(i,1))
                    ok=0;
                if(cnt1!=cnt2) ok=0;
            }
        if(!ok || n%2==1)
        {
            printf("Impossible\n");
            continue;
        }
        nx=ny=n;
        if(Hopcroft_Karp()!=n/2)
        {
            printf("Impossible\n");
            continue;
        }
        cout<<ans<<endl;
        for(int i=1;i<=n;i++)
        {
            if(i==1)
                cout<<i;
            else if(color[i]==color[1])
                cout<<" "<<i;
        }
        cout<<endl;


    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值