HDU 5811 (拓扑排序 LIS)

题目链接:点击这里

题意:给定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
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值