题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=532
题目大意:有n个队伍进行比赛,每场比赛,恰好有一支队伍取胜、一支队伍败。每个队伍需要打的比赛场数相同,给你每个队伍目前已经赢得场数和输得场数,再给你一个矩阵,第 i 行第 j 列 表示队伍 i 和队伍 j 还需要打的比赛数,问你哪些队伍有可能获得冠军(胜场最多的即为冠军,可以并列)。
解题思路:由于队伍数很少,可以想到枚举。那么对于队伍 i 怎么知道他可不可以获得冠军,首先,矩阵中它的比赛肯定是全都赢,这样就能得到它最多的胜场数,然后再判断这个时候,其他队伍胜场会不会都不超过它。那么关键就在于这里如何判断了。
每场比赛只有 u 胜或者 v 胜这两个可能性,把每场比赛看做一个任务,每支队伍看做一个处理器,得到一个“公平分配模型”。
何为“公平分配模型”?即把 m 任务分配给 n 个处理器,每个任务有两个处理器可以选择,这两个处理器保证不相同,可以任选一个分配,要求任务数最多的那个处理器分配的任务最小。
怎么解?这是一个二分图模型,X 表示任务, Y 表示处理器。二分答案 x ,表示这个最多的任务数。从 S 出发引一条边到所有的任务,cap 为 1 。然后从每个任务引两条边分别到对应的两个处理器,cap 为 1。然后再从所有处理器引一条边打 T,cap 为 x 。如果总流量为 m ,那么所有任务都分配掉了,方案可行。
对于这道题,既然这样一个模型抽象出来了,那么就是一样,每两支队伍的比赛是一个 X 节点,每支队伍是一个 Y 节点。从 S 出发引 cap = a[ i ][ j ] 的边到这个 X 节点,a[ i ][ j ] 为 队伍 i 和队伍 j 剩下的比赛数。再从每个 X 节点,引两条边到对应的队伍,cap = INF 。再从每个队伍(Y节点)引一条边到 T,cap = total[ x ] - win[ k ] ,x 为当前枚举的队伍,win[ k ] 为队伍 k 已经赢得的比赛数。判断能不能行,就只要看从 S 出发的每条边是不是都满载了,或者看总流量也可以。另外建边的时候,上面到 T 的边的 cap 如果 < 0,即total[ x ] < win[ k ],那么就是不可能,人家已经赢得比赛都比你多了,直接不可能,要不然这个 cap 就是 负的了。
网络流的题目都是神建模啊!ORZ
代码如下:
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff;
const int MAXN = 33*33*2+33;
const int MAXM = MAXN*MAXN*2;
int w[33],d[33],p[33][33],total[33];
struct Edge
{
int s,t,cap,flow,next;
} edge[MAXM];
int head[MAXN],tot;
void edge_init()
{
tot = 0;
memset(head,-1,sizeof(head));
}
void add_edge(int s,int t,int cap)
{
edge[tot].s = s;
edge[tot].t = t;
edge[tot].cap = cap;
edge[tot].flow = 0;
edge[tot].next = head[s];
head[s] = tot++;
edge[tot].s = t;
edge[tot].t = s;
edge[tot].cap = 0;
edge[tot].flow = 0;
edge[tot].next = head[t];
head[t] = tot++;
}
struct Isap
{
int n;
int s,t;
int hh[MAXN];
int dis[MAXN],num[MAXN],pre[MAXN];
bool vis[MAXN];
void init(int n)
{
this->n = n;
}
int bfs()
{
queue<int> q;
memset(vis,0,sizeof(vis));
vis[t] = 1;
q.push(t);
dis[t] = 0;
while(!q.empty())
{
int x = q.front();
q.pop();
for(int i = head[x];i != -1;i = edge[i].next)
{
Edge& e = edge[i^1];
int to = e.s;
if(!vis[to] && e.cap > e.flow)
{
vis[to] = 1;
dis[to] = dis[x]+1;
q.push(to);
}
}
}
return vis[s];
}
int augment()
{
int x = t,a = INF;
while(x != s)
{
a = min(a,edge[pre[x]].cap-edge[pre[x]].flow);
x = edge[pre[x]].s;
}
x = t;
while(x != s)
{
edge[pre[x]].flow += a;
edge[pre[x]^1].flow -= a;
x = edge[pre[x]].s;
}
return a;
}
int max_flow(int s,int t,int limit)
{
this->s = s;this->t = t;
bfs();
memset(num,0,sizeof(num));
for(int i = 0;i < n;i++) num[dis[i]]++;
for(int i = 0;i < n;i++) hh[i] = head[i];
int x = s;
int flow = 0;
while(dis[s] < n)
{
if(x == t)
{
flow += augment();
if(flow >= limit) break;
x = s;
}
int ok = 0;
for(int i = hh[x];i != -1;i = edge[i].next)
{
Edge& e = edge[i];
int to = e.t;
if(dis[x] == dis[to]+1 && e.cap > e.flow)
{
ok = 1;
hh[x] = i;
pre[to] = i;
x = to;
break;
}
}
if(!ok)
{
int m = n-1;
for(int i = head[x];i != -1;i = edge[i].next)
{
Edge& e = edge[i];
int to = e.t;
if(e.cap > e.flow)
m = min(m,dis[to]);
}
if(--num[dis[x]] == 0) break;
num[dis[x] = m+1]++;
hh[x] = head[x];
if(x != s) x = edge[pre[x]].s;
}
}
return flow;
}
} sol;
int id_s,id_tt,id[MAXN][MAXN],cc;
int get_id(int i,int j)
{
if(j == -1) return cc+i;
else return id[i][j];
}
int build(int x,int n)
{
for(int i = 0;i < n;i++)
for(int j = i+1;j < n;j++)
{
int id = get_id(i,j);
add_edge(id_s,id,p[i][j]);
add_edge(id,get_id(i,-1),INF);
add_edge(id,get_id(j,-1),INF);
}
for(int i = 0;i < n;i++)
{
if(total[x] < w[i])
{
return 0;
}
add_edge(get_id(i,-1),id_tt,total[x]-w[i]);
}
return 1;
}
int check()
{
int ok = 1;
for(int i = head[id_s];i != -1;i = edge[i].next)
{
Edge& e = edge[i];
if(e.cap != e.flow)
{
ok = 0;
break;
}
}
return ok;
}
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int n;
scanf("%d",&n);
for(int i = 0;i < n;i++)
scanf("%d%d",&w[i],&d[i]);
cc = 0;
for(int i = 0;i < n;i++)
{
total[i] = w[i];
for(int j = 0;j < n;j++)
{
scanf("%d",&p[i][j]);
total[i] += p[i][j];
}
}
for(int i = 0;i < n;i++)
for(int j = i+1;j < n;j++)
id[i][j] = cc++;
cc++;
id_s = cc+n,id_tt = id_s+1;
sol.init(id_tt+1);
int first = 1;
for(int i = 0;i < n;i++)
{
edge_init();
int flag = build(i,n);
if(!flag) continue;
sol.max_flow(id_s,id_tt,INF);
if(check())
{
if(first)
{
printf("%d",i+1);
first = 0;
}
else printf(" %d",i+1);
}
}
puts("");
}
return 0;
}
/*
11
4
0 3 3 1 1 3 3 0
0 0 0 2 0 0 1 0 0 1 0 0 2 0 0 0
*/