Boke and Tsukkomi HDU - 4687

A new season of Touhou M-1 Grand Prix is approaching. Girls in Gensokyo cannot wait for participating it. Before the registration, they have to decide which combination they are going to compete as. Every girl in Gensokyo is both a boke (funny girl) and a tsukkomi (straight girl). Every candidate combination is made up of two girls, a boke and a tsukkomi. A girl may belong to zero or more candidate combinations, but one can only register as a member of one formal combination. The host of Touhou M-1 Grand Prix hopes that as many formal combinations as possible can participate in this year. Under these constraints, some candidate combinations are actually redundant as it\'s impossible to register it as a formal one as long as the number of formal combinations has to be maximized. So they want to figure out these redundant combinations and stop considering about them. 



Input
There are multiple test cases. Process to the End of File. 
The first line of each test case contains two integers: 1 ≤ N ≤ 40 and 1 ≤ M ≤ 123, where N is the number of girls in Gensokyo, and M is the number of candidate combinations. The following M lines are M candidate combinations, one by each line. Each combination is represented by two integers, the index of the boke girl 1 ≤ B i ≤ N and the index of the tsukkomi girl 1 ≤ T i ≤ N, where B i != T i.
Output
For each test case, output the number of redundant combinations in the first line. Then output the space-separated indexes of the redundant combinations in ascending order in the second line.
Sample Input
4 4
1 3
2 3
2 4
3 1
6 6
1 2
3 2
3 4
5 2
5 4
5 6
Sample Output
1
2
3

2 4 5


题意:

就是给你n个点,然后再给m个边,每个边表示这两个点构成一个组合,然后问你哪些组合是多余的。

思路:

这肯定是有个一般图匹配,但是他问的是哪些边是多余的,这让我有点懵,然后网上大神们一眼就看出来了该怎么做,反而是我这个菜鸡觉得做法好绕。他问多余的的边是哪些,因为对于一条边就两种可能,多余和不多于,不多余就是少了这个组合不行呗,所以此时可以反着求,先求出来最优匹配数,枚举每一条边,求出删掉这条边之后的匹配数,如过求出来的匹配数比最优解少一,那么这个组合一定是必须的,这样我们就记录了多少必须得了,那么多余的不就是剩下的所有么;但这里有一个技巧,删掉你枚举的边时,不能只是简单的删掉这一条边,因为你删掉这条边时这条边的两个端点还可能和别的点进行匹配,但实际的想法是想让这两个端点形成匹配,不仅仅是要删边,而是删点,将这两个端点删掉,以达到默认这两个端点匹配的情况,接下来再求匹配数;另外一般图匹配的话,肯定就是带花树了;



代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxed=200+10;
struct Node
{
    int u,v;
}node[maxed];
struct E
{
    int v,bef;
}e[maxed*maxed];
queue<int> qu;
int n,m,cnt,ans,head[maxed],answer[maxed];
int gen[maxed],pre[maxed],Mark[maxed],mat[maxed],l_[maxed];
bool vis[maxed];
int main()
{
    //freopen("shuJu.txt","w",stdout);
    void add_(int,int);
    bool slove_2(int);
    while(scanf("%d%d",&n,&m)!=EOF){
        ans=1;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=m;i++){
            scanf("%d%d",&node[i].u,&node[i].v);
            add_(node[i].u,node[i].v);
            add_(node[i].v,node[i].u);
        }
        memset(mat,-1,sizeof(mat));
        int sum=0;
        for(int i=1;i<=n;i++)
            if(mat[i]==-1&&slove_2(i))
                sum++;
        //cout<<sum<<"---"<<endl;
        memset(vis,false,sizeof(vis));
        int sum_=0;
        for(int i=1;i<=m;i++){
            ans=1;
            memset(head,-1,sizeof(head));
            for(int j=1;j<=m;j++){
                if(node[i].u==node[j].u)
                    continue;
                if(node[i].u==node[j].v)
                    continue;
                if(node[i].v==node[j].u)
                    continue;
                if(node[i].v==node[j].v)
                    continue;
                add_(node[j].u,node[j].v);
                add_(node[j].v,node[j].u);
            }
            int w=0;
            memset(mat,-1,sizeof(mat));
            for(int j=1;j<=n;j++)
                if(mat[j]==-1&&slove_2(j))
                    w++;
            if(w==sum-1){
                sum_++;
                vis[i]=true;
            }
        }
        printf("%d\n",m-sum_);
        bool flag=false;
        for(int i=1;i<=m;i++)
            if(!vis[i]){
                if(flag)
                    printf(" ");
                flag=true;
                printf("%d",i);
            }
        printf("\n");
    }
    return 0;
}
void add_(int x,int y)
{
    e[ans].v=y;
    e[ans].bef=head[x];
    head[x]=ans++;
}
int slove_1(int x)
{
    if(gen[x]==x)
        return x;
    return gen[x]=slove_1(gen[x]);
}
bool slove_2(int s)
{
    void slove_3(int);
    void slove_4(int,int);
    for(int i=1;i<=n;i++){
        gen[i]=i;
        Mark[i]=pre[i]=-1;
    }
    Mark[s]=0;
    while(!qu.empty())
        qu.pop();
    qu.push(s);
    while(!qu.empty()){
        int u=qu.front();
        qu.pop();
        for(int i=head[u];i!=-1;i=e[i].bef){
            int v=e[i].v;
            if(slove_1(u)==slove_1(v))
                continue;
            if(Mark[v]==-1){
                Mark[v]=1;
                pre[v]=u;
                if(mat[v]==-1){
                    slove_3(v);
                    return true;
                }
                Mark[mat[v]]=0;
                qu.push(mat[v]);
            }
            else if(!Mark[v])
                slove_4(v,u);
        }
    }
    return false;
}
void slove_3(int p)
{
    while(p!=-1){
        int tmp=mat[pre[p]];
        mat[p]=pre[p];
        mat[pre[p]]=p;
        p=tmp;
    }
}
void slove_4(int x,int y)
{
    void slove_5(int,int,int);
    ++cnt;
    int p=slove_1(x);
    while(1){
        l_[p]=cnt;
        p=mat[p];
        if(p==-1)
            break;
        p=slove_1(pre[p]);
    }
    int lca;
    p=slove_1(y);
    while(1){
        if(l_[p]==cnt){
            lca=p;
            break;
        }
        p=slove_1(pre[mat[p]]);
    }
    slove_5(x,y,lca);
    slove_5(y,x,lca);
}
void slove_5(int x,int y,int lca)
{
    while(slove_1(x)!=lca){
        pre[x]=y;
        if(gen[x]==x)
            gen[x]=lca;
        if(gen[mat[x]]==mat[x])
            gen[mat[x]]=lca;
        if(Mark[mat[x]]==1){
            qu.push(mat[x]);
            Mark[mat[x]]=0;
        }
        y=mat[x];
        x=pre[y];
    }
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值