POJ 2699 The Maximum Number of Strong Kings(最大流)
http://poj.org/problem?id=2699
题意:
一场联赛可以表示成一个完全图,点表示参赛选手,任意两点u, v之间有且仅有一条有向边(u, v)或( v, u),表示u打败v或v打败u。一个选手的得分等于被他打败的选手总数。一个选手被称为“strong king”当且仅当他打败了所有比他分高的选手。分数最高的选手也是strong king。现在给出某场联赛所有选手的得分序列,由低到高,问合理安排每场比赛的结果后最多能有几个strong king。已知选手总数不超过10个。
分析:
首先由于选手最多只有10个,那么我们可以枚举当前strong king的人数x. 假设对于给定的分数,我们枚举了x,那么这x个strong king分别对应给定分数序列的哪几个分数呢?
网上有人说直接必定对应分数最高的那x个人,这种说法不一定正确.但是我们可以证明下列结论:对于一个分数序列,如果最多有k个strong kings,那么必然存在一种比赛情况,就是分数最大的k个人是strong kings.
简略证明如下:
(转自http://blog.csdn.net/sdj222555/article/details/7797257)
假设序列是这个模样,1....i.....j.....k......n
假设只有i,k是strong kings,而j不是. 假设j输给了j+1至n区间中的x个人,那么显然i是赢了这x个人的,我们现在想把j变为strong kings. 那么就让把j输了的这些场全变成赢,此时j分值增加了x,就将j与i之前的人们的比赛多输x场,这样j的分数守恒了,但是j一赢之后,原本输给的x个人的分数少了,那就让他们都去赢i,这样他们的分数也就守恒了,此时发现i分数又不守恒了,少了x,恰好刚才j去输给i之前的人了x场,i正好去赢x场,这样大家的分数都守恒了。
说完了上面的结论,我们每次只需要从大到小枚举分数最高的K个人做strong king即可.现在来建图:
对于每场(ui,vi)之间的比赛i, 从S源点到比赛i有一条容量为1的边.
对于每个选手u,从选手u到T汇点有一条容量为score[u](即u的分数)的边.
对于每场比赛i==(ui,vi)来说:
如果score[ui]<score[vi]且ui是strong king,那么本次比赛结果就定了,必然是ui胜. 所以仅连比赛i 到ui的容量1的边.
同样如果score[ui]>score[vi]且vi是strong king,那么本次比赛结果就定了,必然是vi胜. 所以仅连比赛i到vi的容量1的边.
剩下所有情况都连比赛i分别到ui和vi的容量为1的两条边.(因为比赛结果我们不能肯定)
最终计算最大流,看看该最大流是不是==n*(n-1)/2 ,如果是表示我们枚举的strong king人数合理.
n为比赛人数.程序中用id[i][j]==id[j][i]来表示选手i和j之前进行的那场比赛在网络流图中的节点编号.
AC代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<cctype>
#define INF 1e9
using namespace std;
const int maxn= 1500+10;
struct Edge
{
int from,to,cap,flow;
Edge(){}
Edge(int f,int t,int c,int fl):from(f),to(t),cap(c),flow(fl){}
};
struct Dinic
{
int n,m,s,t;
vector<Edge> edges;
vector<int> G[maxn];
int d[maxn];
bool vis[maxn];
int cur[maxn];
void init(int n,int s,int t)
{
this->n=n,this->s=s,this->t=t;
edges.clear();
for(int i=0;i<n;i++) G[i].clear();
}
void AddEdge(int from,int to,int cap)
{
edges.push_back( Edge(from,to,cap,0) );
edges.push_back( Edge(to,from,0,0) );
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
queue<int> Q;
memset(vis,0,sizeof(vis));
vis[s]=true;
d[s]=0;
Q.push(s);
while(!Q.empty())
{
int x=Q.front(); Q.pop();
for(int i=0;i<G[x].size();i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to] && e.cap>e.flow)
{
vis[e.to]=true;
Q.push(e.to);
d[e.to]=d[x]+1;
}
}
}
return vis[t];
}
int DFS(int x,int a)
{
if(x==t || a==0) return a;
int flow=0,f;
for(int& i=cur[x]; i<G[x].size(); ++i)
{
Edge& e=edges[G[x][i]];
if(d[e.to]==d[x]+1 && (f=DFS(e.to, min(a,e.cap-e.flow) ) )>0 )
{
e.flow +=f;
edges[G[x][i]^1].flow -=f;
flow +=f;
a -=f;
if(a==0) break;
}
}
return flow;
}
int Max_Flow()
{
int ans=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
ans += DFS(s,INF);
}
return ans;
}
}DC;
int n,src,dst;//选手数,源点编号,汇点编号
int num;//流图的最大节点数目
int score[maxn];
int id[maxn][maxn];
void read()//读取每个实例的所有分数
{
char s[1000];
gets(s);
n=0;
for(int i=0;s[i];i++)
{
int temp=0;
if( isdigit(s[i]) )
{
while(s[i] && isdigit(s[i]) )
{
temp=temp*10+s[i]-'0';
++i;
}
--i;//一定要减1,因为i可能跨越了字符串结束字符0
score[++n]=temp;
}
}
}
bool solve(int k)
{
DC.init(num,src,dst);
for(int i=1;i<=n;i++) DC.AddEdge(i,dst,score[i]);
for(int i=n+2;i<num;i++) DC.AddEdge(src,i,1);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
if(score[i]<score[j] && i>= n-k+1) DC.AddEdge(id[i][j], i, 1);
else if(score[j]<score[i] && j>= n-k+1) DC.AddEdge(id[i][j], j, 1);
else
{
DC.AddEdge(id[i][j], i, 1);
DC.AddEdge(id[i][j], j, 1);
}
}
int ans=DC.Max_Flow();
return ans == (n*(n-1)/2);
}
int main()
{
int T;
scanf("%d%*c",&T);
while(T--)
{
read();
src=0,dst=n+1;
num=n+2;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
id[i][j]=id[j][i]=num++;
for(int i=n;i>=0;i--)//枚举strong king可能的个数
{
if(solve(i))
{
printf("%d\n",i);
break;
}
}
}
return 0;
}