又是刘汝佳书上的题,貌似书上只有这两个了,确实2-SAT的题也没有太多,看邝斌的分类也才九个,今天加明天上午再A两个就结束~
做这个题的时候发现自己对于结点的表示还是不够理解,遂把四种情况都列出来
xi为假 或xj为假 | 2*i+1==>2*j | 2*j+1==>2*i |
xi为真或xj为真 | 2*i==>2*j+1 | 2*j==>2*i+1 |
xi为假或xj为真 | 2*i+1==>2*j+1 | 2*j==>2*i |
xi为真或xj为假 | 2*i==>2*j | 2*j+1==>2*i+1 |
上一个飞机调度的题发现两种情况的矛盾了,所以改变其中一个,变成了上表中后两种情况(发现矛盾肯定不能同时改变)
这个宇航员完成任务,限制的条件是相互讨厌的不能去同一个任务,就算不是同样年龄分组的,也不可以同时去C,那么相同年龄分组的就不可以同时去那个A还是B啊。我们假设去A、B是xi=true 去C是xi=true,那么不能同时去C就是和飞机调度一样solver.add_clause(a,1,b,1);,不能同时去A、B就是solver.add_clause(a,0,b,0); 而且感觉从连边的思想上讲,这个题比宇航员好想
/************
uva1391
2016.1.11
255 C++ 4.8.2
************/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define maxn 100004
struct TwoSAT{
int n;
vector<int>G[maxn*2];
bool mark[maxn*2];
int S[maxn*2],c;
bool dfs(int x){
if(mark[x^1]) return false;
if(mark[x]) return true;
mark[x]=true;
S[c++]=x;
for(int i=0;i<G[x].size();i++)
if(!dfs(G[x][i])) return false;
return true;
}
void init(int n){
this->n=n;
for(int i=0;i<(n<<1);i++) G[i].clear();
memset(mark,0,sizeof(mark));
}
void add_clause(int x,int xval,int y,int yval){
x=x*2+xval;
y=y*2+yval;
G[x^1].push_back(y);
G[y^1].push_back(x);
}
bool solve(){
for(int i=0;i<n*2;i+=2)
if(!mark[i]&&!mark[i+1]){
c=0;
if(!dfs(i)){
while(c>0) mark[S[--c]]=false;
if(!dfs(i+1)) {
mark[i]=false;
mark[i+1]=true;
return false;
}
}
}
return true;
}
}solver;
int n,m,sum;
int age[maxn];
int is_young(int x)
{
return age[x]*n<sum;
}
int main()
{
// freopen("cin.txt","r",stdin);
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
sum=0;
for(int i=0;i<n;i++) {
scanf("%d",&age[i]);
sum+=age[i];
}
solver.init(n);///
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
a--;b--;
if(a==b) continue;
solver.add_clause(a,1,b,1);
if(is_young(a)==is_young(b))
solver.add_clause(a,0,b,0);
}
if(!solver.solve())
printf("No solution\n");
else
{
for(int i=0;i<n;i++)
{
if(solver.mark[i<<1]) puts("C");
else if(is_young(i)) puts("B");
else puts("A");
}
}
}
return 0;
}