bzoj4078

二分+2-sat

枚举第一个权值,二分第二个权值,然后2-sat检查,当第一个权值已经不能形成二分图时,再往下没意义,因为没法分成两个点集。(双指针好像跑得慢)

#include<bits/stdc++.h>
using namespace std;
const int N = 410;
struct edge {
    int u, v, w;
    edge(int u = 0, int v = 0, int w = 0) : u(u), v(v), w(w) {}
    bool friend operator < (edge A, edge B)
    {
        return A.w > B.w;
    } 
} e[N * N];
int n, tot, cnt, all, top, ans;
int dfn[N], low[N], st[N], belong[N], vis[N], Map[N][N], color[N], fa[N], d[N];
vector<int> G[N], g[N];
int find(int x)
{
    if(fa[x] == x) return x;
    int ret = find(fa[x]);
    d[x] ^= d[fa[x]];
    return fa[x] = ret;
}
void tarjan(int u)
{
    dfn[u] = low[u] = ++cnt;
    st[++top] = u;
    vis[u] = 1;
    for(int i = 0; i < G[u].size(); ++i)
    {
        int v = G[u][i];
        if(!dfn[v]) 
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(vis[v]) low[u] = min(low[u], low[v]);
    }
    if(low[u] == dfn[u])
    {
        ++all;
        int j;
        while(1)
        {
            j = st[top];
            belong[j] = all;
            vis[j] = 0;
            --top;
            if(j == u) break;
        }
    }
}
bool check(int x, int y)
{
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(vis, 0, sizeof(vis));
    top = 0;
    cnt = 0; 
    all = 0;
    if(x > y) swap(x, y);
    for(int i = 1; i <= 2 * n; ++i) 
        G[i].clear();
    for(int i = 1; i <= tot; ++i)
    {
        if(e[i].w > y)
        {
            G[e[i].u].push_back(e[i].v + n);
            G[e[i].v].push_back(e[i].u + n);
            G[e[i].u + n].push_back(e[i].v);
            G[e[i].v + n].push_back(e[i].u);
        }
        else if(e[i].w > x)
        {
            G[e[i].u].push_back(e[i].v + n);
            G[e[i].v].push_back(e[i].u + n);
        }    
    }
    for(int i = 1; i <= 2 * n; ++i) if(!dfn[i]) tarjan(i);
    for(int i = 1; i <= n; ++i) if(belong[i] == belong[i + n]) return false;
    return true;
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) fa[i] = i;
    for(int i = 1; i < n; ++i)
        for(int j = 1; j <= n - i; ++j)
        {
            scanf("%d", &Map[i][i + j]);
            e[++tot] = edge(i, i + j, Map[i][i + j]);
        } 
    if(n == 1)
    {
        puts("0");
        return 0;
    }
    ++tot;
    sort(e + 1, e + tot + 1);
    ans = 2000000010;
    int j = tot;
    for(int i = 1; i <= tot && i <= j; ++i)
    {
        while(j && !check(e[i].w, e[j].w)) --j;
        int u = find(e[i].u), v = find(e[i].v);
        if(u != v)
        {
            ans = min(ans, e[i].w + e[j].w);
            d[u] = d[e[i].u] ^ d[e[i].v] ^ 1;
            fa[u] = v;
        }
        else if(d[e[i].u] == d[e[i].v])
        {
            ans = min(ans, e[i].w + e[j].w);
            break;
        }
    }
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2017-07-12 21:58  19992147  阅读(272)  评论(0编辑  收藏  举报