首先贪心,对于跟team N比的比赛,都是team N胜,给他加2分,这样就得到team N 的最好成绩记为limit
对于普通的比赛,开始构图了,把每场比赛和每支队伍看做点,增加源点和汇点,源点与每场比赛连上容量为2的边(每场比赛最多给出2分),每场比赛与他的两个参赛队都连上容量为2的边(这样刚好满足三种结局的分配),然后对于每支队伍与汇点直接,如果这支队伍原有的分数大于limit-1,无解,否则连上容量为(limit-1-原有分数)的边
如果最大流满流就是有解,否则无解
/*
题意描述:一个赛区有n支球队,给出这n支球队目前已经赢的场数,
和剩下还要打的场数(该场数包含赛区内与不是本赛区内的),
问球队1有没有可能取得本赛区成绩第一允许并列,每场比赛没有平局的情况
解法(参考网上思路):首先为了让该球对能够赢,那么该球队剩下的场数肯定都要赢,那么该赛区的其他对和别的赛区的比赛都必须输,和该赛区的1队打也必须输,这样就1队拿第一的概率才会增大!然后还有本赛区的两只(不包含1队)队伍打,那么这就使得其中一个队的分数会增加,所以该题主要解决的问题是如何分配比赛的胜负使得最大得分最小!
这里这样建图:建一个源点和汇点,两个对之间有比赛作为一个点称作为比赛点,首先是源点连接比赛点权值为该边的权值,比赛点连接与之关联的两个球队,后每个球队连接汇点,权值为对1的最高分减去当前该队的得分(如果说源点出发的边都是满流那么输出YES否则输出NO)
我们来理解一下这个建图方式:源点到每个比赛点的权值可以看成是总的要用来分配的分数(必须分配完),球队点到汇点的权值表示该球队最多还能分到的分数,如果用来分配的分数没有分配完那么则说明继续分将会使某个队的总得分超过球队1的得分,则说明不可能,若过已经分完那么则说明球队1有可能成为第一
这里注意一点:如果球队1可能的最高得分比某个球队的目前得分还低的话那么则直接输出NO
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=4002;
const int M=30002;
const int INF=1<<29;
int t,n,m,tot;
int gap[M],dis[M],pre[M],head[N],cur[N];
int NE,NV,sink,source,score[M];
struct Node
{
int c,pos,next;
} E[M*4];
#define FF(i,NV) for(int i=0;i<NV;i++)
int sap(int s,int t)
{
memset(dis,0,sizeof(int)*(NV+1));
memset(gap,0,sizeof(int)*(NV+1));
FF(i,NV) cur[i] = head[i];
int u = pre[s] = s,maxflow = 0,aug =INF;
gap[0] = NV;
while(dis[s] < NV)
{
loop:
for(int &i = cur[u]; i != -1; i = E[i].next)
{
int v = E[i].pos;
if(E[i].c && dis[u] == dis[v] + 1)
{
aug=min(aug,E[i].c);
pre[v] = u;
u = v;
if(v == t)
{
maxflow += aug;
for(u = pre[u]; v != s; v = u,u = pre[u])
{
E[cur[u]].c -= aug;
E[cur[u]^1].c += aug;
}
aug =INF;
}
goto loop;
}
}
if( (--gap[dis[u]]) == 0) break;
int mindis = NV;
for(int i = head[u]; i != -1 ; i = E[i].next)
{
int v = E[i].pos;
if(E[i].c && mindis > dis[v])
{
cur[u] = i;
mindis = dis[v];
}
}
gap[ dis[u] = mindis+1 ] ++;
u = pre[u];
}
return maxflow;
}
void addEdge(int u,int v,int c )
{
E[NE].c = c;
E[NE].pos = v;
E[NE].next = head[u];
head[u] = NE++;
E[NE].c = 0;
E[NE].pos = u;
E[NE].next = head[v];
head[v] = NE++;
}
int main()
{
int i,u,v,sum;
while(scanf("%d%d",&n,&m)!=-1)
{
for(i=1;i<=n;++i)scanf("%d",&score[i]);
sink=n+m+1,source=0,NE=0;
memset(head,-1,sizeof(head));
sum=0,NV=sink+1;
for(i=1;i<=m;++i)
{
scanf("%d%d",&u,&v);
if(u==n||v==n)score[n]+=2;
else
{
sum+=2;
addEdge(source,i,2);
addEdge(i,m+u,2);
addEdge(i,m+v,2);
}
}
for(i=1;i<n;++i)
{
if(score[i]>=score[n]-1)break;
addEdge(m+i,sink,score[n]-1-score[i]);
}
if(i==n&&sap(source,sink)==sum)puts("YES");
else puts("NO");
}
return 0;
}