题目链接:点击这里
题意:给定n个人之间的强壮关系(没有传递性),分成两个组,首先判断每一组是否能够通过某种排列使得前面的人都比后面的人强壮。如果可以,最多能从2组中选几个人到1组使得1组通过排列仍然使得前面的人都比后面的人强壮。
因为是个竞赛图,只要没有环就行(DAG),所以直接拓扑如果出队列数刚好等于节点数就可以,在拓扑的时候顺便记下拓扑序给先前的两组按照拓扑序排完。至于最多能够扔过去的点,本来想的是贪心,一个点能够扔到1组当且仅当能打败他的点的最大拓扑序小于他能打败的最小拓扑序。但是有可能把一个点扔过去后导致更多的点扔不过去。所以考虑第二组每个点能够放在第一组的位置,然后第二组所有的点就都有一个位置了,然后求一次LIS就是最多的点数。
O(
n2
)蜜汁超时,加了输入挂和nlgn的LIS才过=。=
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <set>
using namespace std;
#define maxn 1005
#define maxm 2000005
int n, m;
int g[maxn][maxn];
struct E {
int v, next;
}edge[maxm];
int head[maxn], cnt;
bool vis[maxn];
int a[maxn], b[maxn];
int ida[maxn], idb[maxn];
int degree[maxn];//入度
struct node {
int x, y;
bool operator < (const node &a) const {
return x < a.x || (x == a.x && y < a.y);
}
};
set <node> gg;
void add_edge (int u, int v) {
edge[cnt].v = v, edge[cnt].next = head[u], head[u] = cnt++;
}
int q[maxn];
bool topo (int *a, int n, int *id) {
int root = 0;
for (int i = 1; i <= n; i++) {
if (!degree[a[i]]) {
root = a[i];
break;
}
}
int num = 0;
if (!root)
return 0;
num++;
int tou = 0, wei = 0;
q[wei++] = root;
id[root] = 1;
int index = 1;
while (tou < wei) {
int u = q[tou++];
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
degree[v]--;
if (!degree[v]) {
q[wei++] = v;
num++;
id[v] = ++index;
}
}
}
if (num < n)
return 0;
return 1;
}
bool cmp1 (const int &x, const int &y) {
return ida[x] < ida[y];
}
bool cmp2 (const int &x, const int &y) {
return idb[x] < idb[y];
}
int h[maxn], dp[maxn];
#define INF 10000000
int LIS (int *a, int n) {
if (!n)
return 0;
int ans = 0;
for (int i = 1; i <= n; i++) h[i] = INF;
for (int i = 1; i <= n; i++) {
int k = lower_bound (h+1, h+1+n, a[i])-h;
ans = max (ans, k);
h[k] = a[i];
}
return ans;
}
int x[maxn];
void solve () {
//T2
memset (head, -1, (n+10) * sizeof (int));
memset (degree, 0, (n+10) * sizeof (int));
cnt = 0;
for (int i = 1; i <= n-m; i++) {
for (int j = i+1; j <= n-m; j++) {
if (g[b[i]][b[j]]) add_edge (b[i], b[j]), degree[b[j]]++;
else add_edge (b[j], b[i]), degree[b[i]]++;
}
}
if (!topo (b, n-m, idb)) {
printf ("NO\n");
return ;
}
//T1
for (int i = 1; i <= m; i++) {
for (int j = i+1; j <= m; j++) {
if (g[a[i]][a[j]]) add_edge (a[i], a[j]), degree[a[j]]++;
else add_edge (a[j], a[i]), degree[a[i]]++;
}
}
if (!topo (a, m, ida)) {
printf ("NO\n");
return ;
}
printf ("YES ");
gg.clear ();
sort (a, a+m, cmp1);
sort (b, b+n-m, cmp2);
for (int i = 1; i <= n-m; i++) {
int pos1 = 0, pos2 = 0;
int Min = 1000000, Max = 0;//连向它的最大的 它连向的最小的
for (int u = 1; u <= m; u++) {
if (g[a[u]][b[i]]) {
if (ida[a[u]] > Max) {
Max = ida[a[u]];
pos1 = a[u];
}
}
else {
if (ida[a[u]] < Min) {
Min = ida[a[u]];
pos2 = a[u];
}
}
}
if (Min > Max) {
gg.insert ((node) {Max, idb[b[i]]});
}
}
//LIS
int len = 0;
for (set<node>::iterator it = gg.begin (); it != gg.end (); it++) {
x[++len] = it->y;
}
printf ("%d\n", LIS (x, len));
}
int scan () {
char ch = ' ';
while (ch < '0'|| ch > '9') ch = getchar ();
int x = 0;
while (ch <= '9' && ch >= '0') x = x*10+ch-'0', ch = getchar ();
return x;
}
int main () {
while (scanf ("%d%d", &n, &m) == 2 && n+m) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
g[i][j] = scan ();
}
vis[i] = 0;
}
for (int i = 1; i <= m; i++) {
a[i] = scan ();
vis[a[i]] = 1;
}
int cnt = 0;
for (int i = 1; i <= n; i++) if (!vis[i]) {
b[++cnt] = i;
}
solve ();
}
return 0;
}
/*
10 5
0 1 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1
0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 0
1 3 5 7 9
10 3
0 1 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1
0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 0
10 9 8
*/