1. 程式人生 > >POJ 2699 列舉+最大流

POJ 2699 列舉+最大流

這應該說是比較神的一個題目了

題意的話,就是有n 個人,兩兩之間打比賽,每場比賽贏的人加一分,總共呢有n*(n-1)/2個比賽,然後求這樣一種人的個數,就是能贏所有比自己分高的人或者他就是分最高的人。 當然我們是求這種人可能的最大個數,  

建圖的話,就要分兩種點,人和比賽,很顯然,源點到所有的人建邊,容量是該人的得分,所有比賽與匯點建邊,容量為1。那麼現在要處理的就是人與比賽的關係了。

當我們看到n的大小最多隻有10時,就應該意識到這個問題,是否可以列舉呢?

最裸的想法,二進位制位列舉strong kings,那麼複雜度大概是1000*60 * 60,貌似還不錯的樣子。

對每個strong kings,設其編號為i,某個比他分高的人為j,那麼他倆對應的比賽應該是i贏,那麼直接讓i去連線這個比賽點就行了,容量為1,代表這是一個必要的邊,但是其他的比賽我們不知道結果,就要把比賽雙方i,j都連一個容量為1的邊到對應的比賽點上。

實驗一下,125ms

然後就有了進階版本的想法了,可以意識到分數越高的人越有可能成為strong kings,然後我們列舉strong kings的個數k,讓佇列最後k個人成為strong kings。看到網上有人說strong kings'必然是'佇列中的後k個人,這句話顯然是錯的,我們可以通過構造無數不是這樣情況的序列, 但是我們可以確定的是,對於一個序列,如果有k個strong kings,那麼必然存在一種情況,就是後k個人是strong kings。注意,是‘存在’,而不是‘必然是‘。 

現在開始證明 :

假設序列是這個模樣,1....i.....j.....k......n

i,k是strong kings,而j不是

假設j輸給了j+1至n區間中的x個人,那麼顯然i是贏了這x個人的,我們現在想把j變為strong kings

那麼就讓把j輸了的這些場全變成贏,此時分值改變了x,就將與i之前的人們的比賽多輸x場,這樣j的分數守恆了,但是j一贏之後,原本輸給的x個人的分數少了,那就讓他們都去贏i,這樣他們的分數也就守恆了,此時發現i分數又不守恆了,少了x,恰好剛才j去輸給i之前的人了x場,i正好去贏x場,這樣大家的分數都守恆了。

就這樣我們把一個不連續的strong kings們變的緊密了一些,依次這樣,直到strong kings都在序列的後面

然後發現,瞬間變成了0ms

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define MAXN 75
#define MAXM 5555
#define INF 1000000007
using namespace std;
struct node
{
    int ver;    // vertex
    int cap;    // capacity
    int flow;   // current flow in this arc
    int next, rev;
}edge[MAXM];
int dist[MAXN], numbs[MAXN], src, des, n;
int head[MAXN], e;
void add(int x, int y, int c)
{       //e記錄邊的總數
    edge[e].ver = y;
    edge[e].cap = c;
    edge[e].flow = 0;
    edge[e].rev = e + 1;        //反向邊在edge中的下標位置
    edge[e].next = head[x];   //記錄以x為起點的上一條邊在edge中的下標位置
    head[x] = e++;           //以x為起點的邊的位置
    //反向邊
    edge[e].ver = x;
    edge[e].cap = 0;  //反向邊的初始容量為0
    edge[e].flow = 0;
    edge[e].rev = e - 1;
    edge[e].next = head[y];
    head[y] = e++;
}
void rev_BFS()
{
    int Q[MAXN], qhead = 0, qtail = 0;
    for(int i = 1; i <= n; ++i)
    {
        dist[i] = MAXN;
        numbs[i] = 0;
    }
    Q[qtail++] = des;
    dist[des] = 0;
    numbs[0] = 1;
    while(qhead != qtail)
    {
        int v = Q[qhead++];
        for(int i = head[v]; i != -1; i = edge[i].next)
        {
            if(edge[edge[i].rev].cap == 0 || dist[edge[i].ver] < MAXN)continue;
            dist[edge[i].ver] = dist[v] + 1;
            ++numbs[dist[edge[i].ver]];
            Q[qtail++] = edge[i].ver;
        }
    }
}
void init()
{
    e = 0;
    memset(head, -1, sizeof(head));
}
int maxflow()
{
    int u, totalflow = 0;
    int Curhead[MAXN], revpath[MAXN];
    for(int i = 1; i <= n; ++i)Curhead[i] = head[i];
    u = src;
    while(dist[src] < n)
    {
        if(u == des)     // find an augmenting path
        {
            int augflow = INF;
            for(int i = src; i != des; i = edge[Curhead[i]].ver)
                augflow = min(augflow, edge[Curhead[i]].cap);
            for(int i = src; i != des; i = edge[Curhead[i]].ver)
            {
                edge[Curhead[i]].cap -= augflow;
                edge[edge[Curhead[i]].rev].cap += augflow;
                edge[Curhead[i]].flow += augflow;
                edge[edge[Curhead[i]].rev].flow -= augflow;
            }
            totalflow += augflow;
            u = src;
        }
        int i;
        for(i = Curhead[u]; i != -1; i = edge[i].next)
            if(edge[i].cap > 0 && dist[u] == dist[edge[i].ver] + 1)break;
        if(i != -1)     // find an admissible arc, then Advance
        {
            Curhead[u] = i;
            revpath[edge[i].ver] = edge[i].rev;
            u = edge[i].ver;
        }
        else        // no admissible arc, then relabel this vertex
        {
            if(0 == (--numbs[dist[u]]))break;    // GAP cut, Important!
            Curhead[u] = head[u];
            int mindist = n;
            for(int j = head[u]; j != -1; j = edge[j].next)
                if(edge[j].cap > 0)mindist = min(mindist, dist[edge[j].ver]);
            dist[u] = mindist + 1;
            ++numbs[dist[u]];
            if(u != src)
                u = edge[revpath[u]].ver;    // Backtrack
        }
    }
    return totalflow;
}
int id[22][22];
int score[22];
int fg[22][22];
int nt, num;
void cut(char *s)
{
    int len = strlen(s);
    int t = 0;
    for(int i = 0; i < len; i++)
    {
        if(s[i] >= '0' && s[i] <= '9')
        {
            t = t * 10 + s[i] - '0';
            if(i == len - 1 || s[i + 1] == ' ')
            {
                score[++nt] = t;
                t = 0;
            }
        }
    }
}
void build(int k)
{
    init();
    for(int i = 1; i <= nt; i++) add(src, i, score[i]);
    for(int i = nt + 1; i <= num; i++) add(i, des, 1);
    memset(fg, 0, sizeof(fg));
    for(int i = nt - k + 1; i <= nt; i++)
        for(int j = i + 1; j <= nt; j++)
            if(score[i] < score[j])
                add(i, id[i][j], 1), fg[i][j] = 1;
    for(int i = 1; i <= nt; i++)
        for(int j = i + 1; j <= nt; j++)
            if(!fg[i][j])
                add(i, id[i][j], 1), add(j, id[i][j], 1);
}
int main()
{
    int T;
    char s[555];
    scanf("%d", &T);
    getchar();
    while(T--)
    {
        gets(s);
        nt = 0;
        cut(s);
        num = nt;
        for(int i = 1; i <= nt; i++)
            for(int j = i + 1; j <= nt; j++)
                id[i][j] = id[j][i] = ++num;
        src = num + 1;
        des = num + 2;
        n = des;
        int ans = -1;
        for(int i = nt; i >= 1; i--)
        {
            build(i);
            rev_BFS();
            if(maxflow() == nt * (nt - 1) / 2)
            {
                ans = i;
                break;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}