题目大意:有n个人,两两之间有且仅比赛一次,每次比赛有且仅有一个人获胜,获胜的人可以得一分。定义一种人“strong king”,成为“strong king”的条件是,打败了所有得分更高的人,或得分就是最高的。现在,给你这n个人的最终分数,求最多有几个“strong king”。
首先考虑,假设确定了哪些人是“strong king”,哪些人不是(这个需要2^n的枚举)。在这个条件下,判断是否可行。构图。两组点,一组是人,一组是比赛。从源点到每个人建一条边,容量为这个人的得分。再建人到比赛的边,一场比赛(记为j点)对应两个人(记为i1和i2点),只有两种情况。一、这场比赛,随便i1,i2哪个获胜都行,建立i1到j和i2到j两条边,容量都为1。二、这场比赛,有个“strong king”,而且,另外个人得分比他高,这种情况,建立一条“strong king”到j的边,容量也是1。最后,建立每个比赛到汇点的边,容量为1。显然,如果满流,这个方案就是可行的。
优化。其实不用2^n枚举所有人。假设,k个“strong king”的方案是可行的,那么,最高得分的k(或者更多)个人是“strong king”一定是可行的。
证明。按分数从小到大排序。考虑序列1,2...i...j..n-1,.n。假设i为“strong king”,j不为“strong king”。若有一种方案使得,j为“strong king”(i不用考虑是否为“strong king”),那么,原命题则为真。从j+1到n,假设j输给了其中x个人。因为“strong king”的定义,i一定是赢了这x个人的。使得j赢了这x个人,i对这x的比赛改为输。j就成为了“strong king”。这么修改后,等于j多了x分,i少了x分。接着在1到j-1之间,找x个输给j的人(注意,这个x个人原来一定不是“strong king”,因为输给了分数更高的j),让这些比赛胜负取反。接着,除开“strong king”点,将与这些点有关的比赛的输赢不动,修改其他比赛情况,一定可以将得分调整好。这个不好证,我也说不太清楚。感觉应该没问题。因为除开“strong king”后,剩下的图等于没有了限制条件,得分的转移应该没有问题。如果有能说清楚的大神,请不吝赐教。
P.S. 输入的数字之间,居然有不止一个空格的情况。。。
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=1000111222;
const double INFF=1e200;
const double eps=1e-8;
const int mod=1000000007;
const int NN=150;
const int MM=4010;
/* ****************** */
char ss[1005];
int a[12];
struct G
{
int v,cap,next;
}E[3005];
int p[NN],T;
int temp_p[NN],dd[NN],qw[NN];
void add(int u,int v,int cap)
{
E[T].v=v;
E[T].cap=cap;
E[T].next=p[u];
p[u]=T++;
E[T].v=u;
E[T].cap=0;
E[T].next=p[v];
p[v]=T++;
}
bool find_path(int st,int en,int n)
{
int i,head,tail,u,v;
for(i=0;i<=n;i++)
dd[i]=-1;
qw[head=tail=0]=st;
dd[st]=0;
while(head<=tail)
{
u=qw[head++];
for(i=p[u];i+1;i=E[i].next)
{
v=E[i].v;
if(dd[v]==-1 && E[i].cap>0)
{
dd[v]=dd[u]+1;
qw[++tail]=v;
}
}
}
return (dd[en]!=-1);
}
int dfs_flow(int u,int&en,int f)
{
if(u==en || f==0)
return f;
int temp,flow=0;
for(;temp_p[u]+1; temp_p[u] = E[ temp_p[u] ].next)
{
G& e=E[ temp_p[u] ];
if(dd[e.v]==dd[u]+1)
{
temp=dfs_flow(e.v,en,min(f,e.cap));
if(temp>0)
{
e.cap-=temp;
E[ temp_p[u]^1 ].cap+=temp;
flow+=temp;
f-=temp;
if(f==0)
break;
}
}
}
return flow;
}
int dinic(int st,int en,int n)
{
int i,ans=0;
while( find_path(st,en,n) )
{
for(i=0;i<=n;i++)
temp_p[i]=p[i];
ans+=dfs_flow(st,en,INF);
}
return ans;
}
bool ok(int k,int n)
{
memset(p,-1,sizeof(p));
T=0;
int i,j,tol=n;
for(i=1;i<=n;i++)
add(0,i,a[i]);
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
{
tol++;
if(i+k>n && a[j]>a[i])
{
add(i,tol,1);
}
else
{
add(i,tol,1);
add(j,tol,1);
}
}
for(i=n+1;i<=tol;i++)
add(i,tol+1,1);
int ans=dinic(0,tol+1,tol+1);
return (ans==n*(n-1)/2);
}
int main()
{
int n,cas;
int i,l,t;
bool fuck;
scanf("%d",&cas);
getchar();
while(cas--)
{
gets(ss);
n=0;
l=strlen(ss);
t=0;
fuck=false;
for(i=0;i<=l;i++)
{
if(ss[i]>='0' && ss[i]<='9')
{
t=t*10+ss[i]-'0';
fuck=true;
}
else
{
if(fuck)
a[++n]=t;
fuck=false;
t=0;
}
}
sort(a+1,a+1+n);
for(i=n;i>0;i--)
{
if( ok(i,n) )
break;
}
printf("%d\n",i);
}
return 0;
}