CodeForces - 687A NP-Hard Problem(二分图染色模板+链式前向星存图)

NP-Hard Problem

Recently, Pari and Arya did some research about NP-Hard problems and they found the minimum vertex cover problem very interesting.

Suppose the graph G is given. Subset A of its vertices is called a vertex cover of this graph, if for each edge uv there is at least one endpoint of it in this set, i.e. or (or both).

Pari and Arya have won a great undirected graph as an award in a team contest. Now they have to split it in two parts, but both of them want their parts of the graph to be a vertex cover.

They have agreed to give you their graph and you need to find two disjoint subsets of its vertices A and B, such that both A and B are vertex cover or claim it’s impossible. Each vertex should be given to no more than one of the friends (or you can even keep it for yourself).

Input
The first line of the input contains two integers n and m (2 ≤ n ≤ 100 000, 1 ≤ m ≤ 100 000) — the number of vertices and the number of edges in the prize graph, respectively.

Each of the next m lines contains a pair of integers u i and v i (1  ≤  u i,  v i  ≤  n), denoting an undirected edge between u i and v i. It’s guaranteed the graph won’t contain any self-loops or multiple edges.

Output
If it’s impossible to split the graph between Pari and Arya as they expect, print “-1” (without quotes).

If there are two disjoint sets of vertices, such that both sets are vertex cover, print their descriptions. Each description must contain two lines. The first line contains a single integer k denoting the number of vertices in that vertex cover, and the second line contains k integers — the indices of vertices. Note that because of m ≥ 1, vertex cover cannot be empty.

Examples
Input
4 2
1 2
2 3
Output
1
2
2
1 3
Input
3 3
1 2
2 3
1 3
Output
-1
Note
In the first sample, you can give the vertex number 2 to Arya and vertices numbered 1 and 3 to Pari and keep vertex number 4 for yourself (or give it someone, if you wish).

In the second sample, there is no way to satisfy both Pari and Arya.

题意: 告诉你有n个点标号从1到n,再告诉你m条边的信息,问你将这幅图分成两个满足题意的部分,两个集合中不能有相联系的点,就是将一条边的两个顶点分别放入两个集合中,二分图可以满足这个要求。

解题思路: 将这个图进行染色,一种颜色一个集合,但有的点不会被染色就像第一个样例中的4,可以放进任意一个集合也可以选择都不放。

#include<list>
#include<string.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
const int Base =131;
const ll INF = 1ll << 62;
//const double PI = acos(-1);
const double eps = 1e-7;
const int mod = 1e9+7;
#define debug puts("**********");
#define mem(a,b) memset(a,b,sizeof(a))
#define speed {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); }

int n,m,cnt=0,head[maxn],color[maxn];
struct Edge//链式前向星
{
    int v,next;
}e[maxn<<1];

vector<int>save1,save2;

void AddEdge(int u,int v)
{
    e[cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

bool dfs(int u,int c)
{
    color[u]=c;//染色
    for(int i=head[u];i!=-1;i=e[i].next)//遍历与u相关的点
    {
        int v=e[i].v;
        if(color[v]==c)return false;//如果u点的颜色与v点的颜色相同说明不存在题意所述的图
        if(!color[v]&&!dfs(v,-c))return false;//如果v点未染色,但与自己相关联的点有问题说明不存在题意所述的图
    }
    return true;
}

int main()
{
    mem(head,-1);//链式前向星初始化
    mem(color,0);
    scanf("%d %d",&n,&m);
    while(m--)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        AddEdge(a,b);
        AddEdge(b,a);
    }
    for(int i=1;i<=n;i++)//对每个点进行处理
        if(head[i]>-1&&!color[i])
        {
            if(!dfs(i,1))
            {
                printf("-1\n");
               // system("pause");
                return 0;
            }
        }

    for(int i=1;i<=n;i++)
    {
        if(color[i]==1)save1.push_back(i);
        if(color[i]==-1)save2.push_back(i);
    }
    printf("%d\n",save2.size());
	for(int i=0;i<save2.size();i++)printf("%d%c",save2[i]," \n"[i==save2.size()-1]);
	printf("%d\n",save1.size());
	for(int i=0;i<save1.size();i++)printf("%d%c",save1[i]," \n"[i==save1.size()-1]);

   // system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值