LightOJ 1251 Forming the Council【2-Sat+逆向拓扑排序输出可行解】好题!

本文介绍了一个基于2-SAT问题的选举模拟算法,旨在解决如何形成一个让所有选民满意的政府委员会的问题。通过构建特定的图模型,并运用Tarjan算法寻找强连通分量,进而确定每个候选人的当选状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1251 - Forming the Council
Time Limit: 2 second(s)Memory Limit: 32 MB

In a city there are n voters, and m people formed the Govt. council. The council members are numbered from 1 to m. Now everyone is complaining that the council is biased. So, they made a plan. The plan is that the voters are given a chance to vote again to form the new council. A vote will be like ±i ±j. '+' means the voter wants that member to be in the council, '-' means the voter doesn't want the member to be in the council. For example, there are 4 voters, they voted like

+1 -3    the voter wants member 1 to be kept in the council or member 3 to be thrown out

+2 +3  the voter wants member 2 to be kept in the council or member 3 to be kept in the council

-1 -2     the voter wants member 1 to be thrown out or member 2 to be thrown out

-4 +1    the voter wants member 4 to be thrown out or member 1 to be kept in the council

A voter will be satisfied if at least one of his wishes becomes true. Now your task is to form the council such that all the voters are happy.

Input

Input starts with an integer T (≤ 20), denoting the number of test cases.

Each case starts with a line containing two integers n (1 ≤ n ≤ 20000) and m (1 ≤ m ≤ 8000). Each of the next n lines contains a vote in the form ±i ±j (1 ≤ i, j ≤ m).

Output

For each case, print the case number and 'Yes' if a solution exists, or 'No' if there is no solution. Then if the result is yes, print another line containing the number of members in the council followed by the members in ascending order. And print a single space between two numbers. There can be many solutions. Any valid one will do.

Sample Input

Output for Sample Input

3

4 3

+1 +3

+2 -1

+2 -3

-1 -2

4 2

+1 -2

+1 +2

-1 -2

-1 +2

1 3

+1 -3

Case 1: Yes

2 2 3

Case 2: No

Case 3: Yes

0

Note

This is a special judge problem. Wrong output format may cause wrong answer.


PROBLEM SETTER: JANE ALAM JAN

题目大意:

一共有N个选民,M个参选者,一个选民有两个意向,对于+ 表示希望这个人当选。 - 表示希望这个人落选,至少满足一条的结果,对于这个选民来讲就是满足的。

问是否有一个可行解,使得所有选民都满足。

如果有,输出当选的人的编号。


思路:


1、很明显的2-Sat问题。首先我们将一个参选者的当选和不当选进行拆点。

i代表第i个人当选了,i+n代表这个人没有当选。

那么这个问题就变成了一个经典的2-Sat问题。


2、2-Sat问题在于找寻矛盾边,只要矛盾边找寻对了,那么2-Sat的模型和图就建好了。

很明显,对于一个选民来讲,如果两条条件都没有满足,那么很明显这就是矛盾边。

那么对于各种情况,我们建边:



3、接下来跑Tarjan强连通,对应判断是否存在可行解。

对于存在可行解的情况,我们对于缩点之后的图进行反向建图。

然后拓扑排序。

将对立事件进行染色即可。

统计答案进行输出。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
int output[40005];
int vis[70005];
int low[70005];
int dfn[70005];
int print[70005];
int stack[70005];
int color[70005];
int pos[70005];
int degree[70005];
vector<int >mp[70005];
vector<int >mp2[70005];
int n,m,sig,cnt,tot,cont;
void add(int x,int y)
{
    mp[x].push_back(y);
}
void top()
{
    memset(print,0,sizeof(print));
    queue<int >s;
    for(int i=1;i<=sig;i++)
    {
        if(degree[i]==0)
        {
            s.push(i);
        }
    }
    while(!s.empty())
    {
        int u=s.front();
        if(print[u]==0)
        {
            print[u]=1;print[pos[u]]=2;
        }
        s.pop();
        for(int i=0;i<mp2[u].size();i++)
        {
            int v=mp2[u][i];
            degree[v]--;
            if(degree[v]==0)s.push(v);
        }
    }
    cont=0;
    for(int i=1;i<=n;i++)if(print[color[i]]==1)output[cont++]=i;
}
void Tarjan(int u)
{
    vis[u]=1;
    dfn[u]=low[u]=cnt++;
    stack[++tot]=u;
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(vis[v]==0)Tarjan(v);
        if(vis[v]==1)low[u]=min(low[u],low[v]);
    }
    if(low[u]==dfn[u])
    {
        sig++;
        do
        {
            vis[stack[tot]]=-1;
            color[stack[tot]]=sig;
        }
        while(stack[tot--]!=u);
    }
}
int Slove()
{
    sig=0;
    cnt=1;
    tot=-1;
    memset(degree,0,sizeof(degree));
    memset(stack,0,sizeof(stack));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(color,0,sizeof(color));
    for(int i=1;i<=n*2;i++)
    {
        if(vis[i]==0)
        {
            Tarjan(i);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(color[i]==color[i+n])return 0;
        pos[color[i]]=color[i+n];
        pos[color[i+n]]=color[i];
    }
    for(int i=1;i<=n*2;i++)
    {
        for(int j=0;j<mp[i].size();j++)
        {
            int v=mp[i][j];
            if(color[i]!=color[v])
            {
                degree[color[i]]++;
                mp2[color[v]].push_back(color[i]);
            }
        }
    }
    top();
    return 1;
}
int main()
{
    int t;
    int kase=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&m,&n);
        for(int i=1;i<=60000;i++)mp[i].clear(),mp2[i].clear();
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            int xx=x;int yy=y;
            if(x<0)x=-x;
            if(y<0)y=-y;
            if(xx>0&&yy>0)add(x+n,y),add(y+n,x);
            if(xx>0&&yy<0)add(x+n,y+n),add(y,x);
            if(xx<0&&yy>0)add(x,y),add(y+n,x+n);
            if(xx<0&&yy<0)add(x,y+n),add(y,x+n);
        }
        int ans=Slove();
        printf("Case %d: ",++kase);
        if(ans==1)
        {
            printf("Yes\n");
            printf("%d",cont);
            for(int i=0;i<cont;i++)
            {
                printf(" %d",output[i]);
            }
            printf("\n");
        }
        else printf("No\n");
    }
}








评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值