题意:有A,B,C三个任务要分给n个宇航员,年龄大于等于平均年龄的分A, 年龄小于平均年龄的分在B,C组没有界限,互为敌人的两个宇航员不可以在同一组.求出一个分配的方案。
思路:可以发现每个宇航员只有两种选择,选c或者不选c,用一个布尔变量xi代表第i个宇航员的选择,
对于相互为敌的两个年龄同时大于等于或同时小于平均年龄的宇航员,需要满足!(xi && xj)和!((!xi) && !(!xj))
对于年龄在平均年龄两侧的两个宇航员,只需要满足!(xi && xj),得到这些条件就可以建图然后用2-SAT算法求得答案。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#define eps 1e-6
#define LL long long
using namespace std;
const int maxn = 100000 + 1000;
//const int INF = 0x3f3f3f3f;
//2-sat
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*2; 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)) return false;
}
}
}
return true;
}
} solver;
int n, m, age[maxn];
int main() {
//freopen("input.txt", "r", stdin);
while(scanf("%d%d", &n, &m)==2 && n) {
solver.init(n);
int su = 0, ave;
for(int i = 0; i < n; i++) scanf("%d", &age[i]), su += age[i];
ave = su%n==0 ? su/n : su/n+1;
for(int i = 0; i < m; i++) {
int u, v; scanf("%d%d", &u, &v);
u--; v--;
if(age[u]<ave&&age[v]<ave || age[u]>=ave&&age[v]>=ave) {
solver.add_clause(u, 0, v, 0);
solver.add_clause(u, 1, v, 1);
}
else {
solver.add_clause(u, 0, v, 0);
}
}
if(!solver.solve()) puts("No solution.");
else {
for(int i = 0; i < n; i++) {
if(solver.mark[2*i]) {
if(age[i] >= ave) puts("A");
else puts("B");
}
else puts("C");
}
}
}
return 0;
}