UVAlive 7037 - The Problem Needs 3D Arrays(网络流‘最大密度子图)

题目:

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=94761#problem/C

题意:

序列T,r(S)表示子序列S(不连续)中的逆序数对,L(S) 表示S 的长度,求出 r(S) / L(S) 的最大值

思路:

r(S) 可以看成边,L(S) 可以看成点,使得 E / V 最大,则题目转化为 求解 最大密度子图。

存在逆序数对(u,v),二分答案g, 建边:

1. s 与 边 id 建一条容量的1 的边。

2. id与u点 建一条容量为inf 的边,id与v点建一条容量为inf的边。

3. u 与t 建一条容量为g 的边。

使得 res = 逆序数对总数 - maxflow 趋于0,则此时g 为答案。

AC.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const int maxn = 7000;
const int maxm = 1e7;
int n;
int val[maxn];

int gap[maxn], dep[maxn], cur[maxn];

int tot, head[maxn];
struct Edge{
    int to, next;
    double cap, flow;
}edge[maxm];
void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, double w)
{
    edge[tot].to = v; edge[tot].cap = w; edge[tot].flow = 0.0;
    edge[tot].next = head[u]; head[u] = tot++;

    edge[tot].to = u; edge[tot].cap = 0.0; edge[tot].flow = 0.0;
    edge[tot].next = head[v]; head[v] = tot++;
}

int Q[maxn];
void bfs(int start, int endd)
{
    memset(dep, -1, sizeof(dep));
    memset(gap, 0, sizeof(gap));
    gap[0] = 1;
    int fron = 0, rear = 0;
    dep[endd] = 0;
    Q[rear++] = endd;

    while(fron != rear) {
        int u = Q[fron++];
        for(int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if(dep[v] != -1) continue;
            Q[rear++] = v;
            dep[v] = dep[u] + 1;
            gap[dep[v]]++;
        }
    }
}
int S[maxn];
double sap(int start, int endd, int N)
{
    bfs(start, endd);
    memcpy(cur, head, sizeof(head));
    int top = 0;
    int u = start;
    double ans = 0;
    while(dep[start] < N) {
        if(u == endd) {
            double Min = inf;
            int inser;
            for(int i = 0; i < top; ++i) {
                if(Min > edge[S[i]].cap -edge[S[i]].flow) {
                    Min = edge[S[i]].cap - edge[S[i]].flow;
                    inser = i;
                }
            }
            for(int i = 0; i < top; ++i) {
                edge[S[i]].flow += Min;
                edge[S[i]^1].flow -= Min;
            }
            ans += Min;
            top = inser;
            u = edge[S[top]^1].to;
            continue;
        }
        bool flag = 0;
        int v;
        for(int i = cur[u]; ~i; i = edge[i].next) {
            v = edge[i].to;
            if(edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
                flag = true;
                cur[u] = i;
                break;
            }
        }
        if(flag) {
            S[top++] = cur[u];
            u = v;
            continue;
        }
        int Min = N;
        for(int i =  head[u]; ~i; i = edge[i].next) {
            if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
                Min = dep[edge[i].to];
                cur[u] = i;
            }
        }
        gap[dep[u]]--;
        if(!gap[dep[u]]) return ans;
        dep[u] = Min + 1;
        gap[dep[u]]++;
        if(u != start) u = edge[S[--top]^1].to;
    }
    return ans;
}

//int du[maxn];
//void build(int s, int t, int sum, double g)
//{
//    init();
//    for(int i = 1; i <= n; ++i) {
//        for(int j = i+1; j <= n; ++j) {
//            addedge(i, j, 1.0);
//
//           // addedge(j, i, 1.0);
//        }
//    }
//
//    for(int i = 1; i <= n; ++i) {
//        addedge(s, i, sum);
//        addedge(i, t, sum*1.0+2*g-du[i]*1.0);
//    }
//}

struct node{
    int u, v;
}g[maxm];
void build(int s, int t, double mid, int sum)
{
    init();

    for(int i = 1; i <= sum; ++i) {
        addedge(s, i, 1);
        addedge(i, g[i].u+sum, inf);
        addedge(i, g[i].v+sum, inf);
    }
    for(int i = 1; i <= n; ++i) {
        addedge(i+sum, t, mid);
    }
}


int main()
{
    //freopen("in", "r", stdin);
    int T, ca = 1;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &val[i]);
        }

        int sum = 1;
        for(int i = 1; i <= n; ++i) {
            for(int j = i+1; j <= n; ++j) {
                if(val[i] > val[j]) {
                    g[sum].u = i;
                    g[sum++].v = j;
                }
            }
        }

        sum--;
        int s = 0, t = sum + n + 1;
        double l = 0, r = sum;
        double ans = 0;
        while((r-l) > eps) {
            double mid = (l+r) / 2.0;
            build(s, t, mid, sum);
            double res = 1.0*sum - sap(s, t, t+1);

            if(res >= eps) {
                ans = mid;
                l = mid;
            }
            else r = mid;
        }

        printf("Case #%d: %.12lf\n", ca++, ans);
    }
    return 0;
}

第二种建图:

原图拆成两条有向边容量为1,源点到每一点建一条容量为sum的边,每一点到汇点建边容量为sum + 2*mid - du[v], du[v] 为v点的度。

二分答案,当 sum*n  - sap(s,t) 趋近于0, 得到答案。

AC.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const int maxn = 7000;
const int maxm = 1e7;
int n;
int val[maxn];

int gap[maxn], dep[maxn], cur[maxn];

int tot, head[maxn];
struct Edge{
    int to, next;
    double cap, flow;
}edge[maxm];
void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, double w)

int Q[maxn];
void bfs(int start, int endd)
int S[maxn];
double sap(int start, int endd, int N)


int du[maxn];
struct node{
    int u, v;
}g[maxn];

void build(int s, int t, int sum, double mid)
{
    init();
    for(int i = 1; i <= sum; ++i) {
        addedge(g[i].u, g[i].v, 1);
        addedge(g[i].v, g[i].u, 1);
    }

    for(int i = 1; i <= n; ++i) {
        addedge(s, i, sum);
        addedge(i, t, sum*1.0+2*mid-du[i]*1.0);
    }

}

int main()
{
    //freopen("in", "r", stdin);
    int T, ca = 1;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &val[i]);
        }

        memset(du, 0, sizeof(du));
        int sum = 1;
        for(int i = 1; i <= n; ++i) {
            for(int j = i+1; j <= n; ++j) {
                if(val[i] > val[j]) {
                    g[sum].u = i;
                    g[sum++].v = j;
                    du[i]++;
                    du[j]++;
                }
            }
        }

        sum--;
        int s = 0, t = n + 1;
        double l = 0, r = sum;
        double ans = 0;
        while((r-l) > eps) {
            double mid = (l+r) / 2.0;
            build(s, t, sum, mid);
            double tmp = sap(s, t, t+1);
            double res = 1.0*sum*n - tmp;

            if(res >= eps) {
                ans = mid;
                l = mid;
            }
            else r = mid;
        }

        printf("Case #%d: %.12lf\n", ca++, ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值