容易想到二分, 问题是如何 check
发现对于一个点, 他能够选的只有两种, 对于他连的点, 能够选的也只有两种
又因为总共只有三种方案, 那么一条边连的两个点最少会有一种一样的
那么这个选了这种, 那个就不能选这种, 那个选了这种, 这个就选不了这种
嗯, 是个 2 - sat
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int N = 1005;
using namespace std;
int n, a[N], f[N][N], head[N << 1], dfn[N << 1], low[N << 1], stk[N << 1], top, instk[N << 1], cnt, tot, bl[N << 1], id[N][2], num[N][2], ans;
struct edge { int to, nxt; } e[N * N * 2];
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
inline void adde(int u, int v) { e[++cnt] = (edge) { v, head[u] }, head[u] = cnt; }
void tarjan(int u)
{
dfn[u] = low[u] = ++cnt, instk[stk[++top] = u] = 1;
for(int v, i = head[u]; i; i = e[i].nxt)
{
v = e[i].to;
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(instk[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] >= dfn[u])
{
tot++; int x;
do
{
instk[x = stk[top--]] = 0;
bl[x] = tot;
}
while(x != u);
}
}
void clear()
{
memset(dfn, 0, sizeof(dfn)), memset(low, 0, sizeof(low));
memset(head, 0, sizeof(head)), cnt = tot = 0;
}
bool check(int mid)
{
clear();
for(int x, y, i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
if(f[i][j] > mid)
{
if(id[i][0] == id[j][0] || id[i][0] == id[j][1])
x = 0, y = id[i][0] == id[j][1];
else x = 1, y = id[i][1] == id[j][1];
adde(num[i][x], num[j][y ^ 1]), adde(num[j][y], num[i][x ^ 1]);
if(id[i][x ^ 1] == id[j][y ^ 1])
adde(num[i][x ^ 1], num[j][y]), adde(num[j][y ^ 1], num[i][x]);
}
cnt = 0;
for(int i = 1; i <= 2 * n; i++)
if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n; i++)
if(bl[num[i][0]] == bl[num[i][1]]) return 0;
return 1;
}
int main()
{
n = read <int> ();
for(int i = 1; i <= n; i++)
{
a[i] = read <int> (), num[i][1] = i, num[i][0] = i + n;
id[i][0] = (!a[i] ? 1 : 0), id[i][1] = (a[i] == 2 ? 1 : 2);
for(int x, j = 1; j < n; j++)
x = read <int> (), f[i][x] = j;
}
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
f[i][j] = max(f[i][j], f[j][i]);
int l = 1, r = n;
while(l <= r)
{
int mid = (l + r) >> 1;
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", ans);
return 0;
}
原文:https://www.cnblogs.com/ztlztl/p/12296709.html