uva1086 The Ministers' Major Mess

 题意:有n 个议案,m 个大臣,每个大臣会对其中的ki 个议案投票,为赞成或反对。现要你判断是否存在一种方案,使得每个大臣有大于一半的投票被满足。若存在,还需判断某个议案是不是一定要通过,或者一定不能通过。

数据范围:n≤1000,m≤5000,1≤ki≤4

 

首先这是一些布尔变量的真假的问题,这让我们联想到2-SAT

仔细观察题意发现,k=1,2的时候所投的1个/2个提议的结果必须和投的票相同,k=3和4的时候最多允许有一个提议的结果和投的票不同.

因为2-SAT问题是对两个变量的约束,我们发现,如果k=3,某个人要求A,B,C三个变量至少两个为真,那么这三个变量中任意两个至少一个为真,也就是”A或B”,”B或C”,”A或C”

K=4的时候,添加6条限制即可.

接下来枚举每个变量的取值,分别添加某个变量必须为真/假的条件,用线性做法做2n次2-SAT,然后这样是卡在1s左右.我们发现,只要找出一个可行方案,就能对n个变量都给出一个可行的取值,然后就把2-SAT次数减少到了n次,就可以过了

吐槽:考试的题是单组数据,UVA上多组数据,输出格式还不一样…加初始化,改输出格式的时候挂了5次(最有趣的一次是考试的时候没有输出文末回车因为是单组数据没出事,多组数据的时候就成了每组数据的输出之间没有回车…)

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=2005,maxm=500005;
struct edge{
    int to,next;
}lst[maxm],lst2[maxm];int len=1,first[maxn],len2=1,first2[maxn];
void addedge(int a,int b){//printf("%d %d\n",a,b);
    lst[len].to=b;lst[len].next=first[a];first[a]=len++;
}
void addedge2(int a,int b){//printf("%d->%d\n",a,b);
    lst2[len2].to=b;lst2[len2].next=first2[a];first2[a]=len2++;
}
int conv[maxn][2];int ok[maxn][2];
void AddTrue(int x){
    addedge(conv[x][0],conv[x][1]);
}
void AddFalse(int x){
    addedge(conv[x][1],conv[x][0]);
}
void AddOr(int x1,int t1,int x2,int t2){
    addedge(conv[x1][t1^1],conv[x2][t2]);addedge(conv[x2][t2^1],conv[x1][t1]);
}
int n,m,k;
int a[10],b[10];char buf[5];
int s[maxn],top,dfn[maxn],low[maxn],belong[maxn],tot,T;
bool ins[maxn];
void dfs(int x){
    s[top++]=x;ins[x]=true;
    dfn[x]=low[x]=++T;
    for(int pt=first[x];pt;pt=lst[pt].next){
        if(!dfn[lst[pt].to]){
            dfs(lst[pt].to);
            if(low[lst[pt].to]<low[x])low[x]=low[lst[pt].to];
        }else if(ins[lst[pt].to]&&dfn[lst[pt].to]<low[x])low[x]=dfn[lst[pt].to];
    }
    if(low[x]==dfn[x]){
        ++tot;
        do{
            ins[s[--top]]=false;belong[s[top]]=tot;
        }while(s[top]!=x);
    }
}
bool Tarjan(){
    memset(dfn,0,sizeof(dfn));T=0;tot=0;
    for(int i=1;i<=n;++i){
        if(!dfn[conv[i][0]])dfs(conv[i][0]);
        if(!dfn[conv[i][1]])dfs(conv[i][1]);
        if(belong[conv[i][0]]==belong[conv[i][1]])return false;
    }
    return true;
}

int sz[maxn];int choose[maxn];bool vis[maxn];
int toposeq[maxn];int cnt=0;
void toposort(int x){
    if(vis[x])return;
    vis[x]=true;
    for(int pt=first2[x];pt;pt=lst2[pt].next){
        toposort(lst2[pt].to);
    }
    toposeq[++cnt]=x;
}
int neg(int x){
    return (x&1)?(x+1):(x-1);
}
void get_solution(){
    vector<int> scc[maxn];
    int lim=2*n;cnt=0;
    for(int i=1;i<=lim;++i)sz[belong[i]]++,scc[belong[i]].push_back(i);
    for(int i=1;i<=lim;++i){
        for(int pt=first[i];pt;pt=lst[pt].next){
            if(belong[i]!=belong[lst[pt].to]){
                addedge2(belong[i],belong[lst[pt].to]);
            }
        }
    }
    for(int i=1;i<=tot;++i){
        if(!vis[i])toposort(i);
    }
    for(int i=1;i<=cnt;++i){
        int x=toposeq[i];int flag=1;//printf("%d\n",x);
        if(choose[x]!=0)continue;
        for(int pt=first2[x];pt;pt=lst2[pt].next){
            if(choose[lst2[pt].to]==-1){
                flag=-1;break;
            }
        }
        choose[x]=flag;
        for(int j=0;j<sz[x];++j){
            choose[belong[neg(scc[x][j])]]=-flag;
        }
    }//printf("%d %d\n",choose[belong[conv[2][0]]],choose[belong[conv[2][1]]]);
    for(int i=1;i<=n;++i){
        if(choose[belong[conv[i][0]]]==1){
            ok[i][0]=1;
        }else{
            ok[i][1]=1;
        }
    }
}
void work(){
    for(int i=n;i>=1;--i){
        for(int k=0;k<2;++k){
            if(ok[i][k]==1)continue;
            int tmp=first[conv[i][k^1]];
            if(k==1)AddTrue(i);
            else AddFalse(i);
            if(Tarjan()){
                ok[i][k]=1;
            }
            len--;
            first[conv[i][k^1]]=tmp;    
        }
    }
}
int main(){
    int t=0;
    while(scanf("%d%d",&n,&m),n!=0){
       
        memset(first,0,sizeof(first));len=1;memset(first2,0,sizeof(first2));len2=1;
        memset(conv,0,sizeof(conv));memset(ok,0,sizeof(ok));
        memset(choose,0,sizeof(choose));memset(vis,0,sizeof(vis));
        memset(sz,0,sizeof(sz));
        for(int i=1;i<=n;++i){
            conv[i][0]=2*i-0;conv[i][1]=2*i-1;
        }
        for(int i=1;i<=m;++i){
            scanf("%d",&k);
            for(int j=1;j<=k;++j){
                scanf("%d",&a[j]);
                scanf("%s",buf);
                if(buf[0]=='y')b[j]=1;
                else b[j]=0;
            }
            if(k==1){
                if(b[1]==1)AddTrue(a[1]);
                else AddFalse(a[1]);
            }else if(k==2){
                for(int j=1;j<=2;++j){
                    if(b[j]==1)AddTrue(a[j]);
                    else AddFalse(a[j]);
                }
            }else{
                for(int j=1;j<=k;++j){
                    for(int l=j+1;l<=k;++l){
                        AddOr(a[j],b[j],a[l],b[l]);
                    }
                }
            }
        }    
        printf("Case %d: ",++t);      
        if(Tarjan()){
            get_solution();
            work();
            for(int i=1;i<=n;++i){
                if(ok[i][0]==1&&ok[i][1]==1)printf("?");
                else printf("%c",(ok[i][0]==1)?'n':'y');
            }printf("\n");
        }else{
            puts("impossible");
        }
    }
    return 0;
}

 

posted @ 2017-02-15 15:39  liu_runda  阅读(508)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难