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; }