停摆??停摆你妹啊!!2011-2012NBA赛季开已经打啦!你OUT了。
有n支队伍比赛,n<=20。已经打了一些比赛,并且知道了A赛区的队伍的目前得分。队伍i的目前得分为score[i],剩余比赛场次为remain[i],剩余场次包括同赛区和异赛区的比赛。用match[i][j]表示A区队伍的剩余的比赛情况,i和j剩余的比赛场数。当然,remain[i]>=sigma{match[i][k],1<=k<=n}。要知道,篮球比赛是没有平局的,问队伍1可不可能在赛季末获得A区的第一名(可以并列第一)?
解:
1. 显然,有种情况是:当队伍1赢下剩下所有比赛,其余队伍输掉剩余比赛,队伍1也不可能赢得第一。
2. 增设源点s和汇点t,两类点:左边点代表队伍,右边点代表某两个队伍之间的比赛。
s到第一类点连边,容量为那支队伍还可以赢得的最多的比赛场数,即(队伍1目前得分+队伍1的剩余比赛场数-队伍i的目前得分);
第二类点到t连边,容量为该点代表的两只队伍剩余的比赛场数K;
第一类点到第二类点:若队伍i和队伍j之间有比赛,且对应的第二类点的标号为p,则连边(i,p,INF),(j,p,INF)。
3. 求最大流。若第二类点到t的边都满流,就可以找到一组解,否则无解。
为何这样建图是对的呢?可以了解到建图的形式为S-->每个队-->比赛-->T,就是把剩余的比赛场数分配到每个队中,每个队的得分不能超过限制。当然这个限制还不够,还需要最小割全部切到与T相连的边上;否则,最小割肯定切到至少两个队伍上的边,而且要打的比赛>0,这样,本来这两个队的得分已经==队伍1的最高得分,再打比赛,肯定会有一个队超过限制,这样队伍1就不肯能获得冠军了。
复杂度分析:点数最多有2+n+(n+1)*n/2<=232,显然用最大流是合适的。听说有更好的方法。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int INF = 0x3f3f3f3f; const int maxv = 1000; const int maxe = 10000; int n; int score[30]; int remain[30]; int match[30][30]; // struct Edge { int v; int next; int flow; }; Edge e[maxe]; int head[maxv],edgeNum; int now[maxv],d[maxv],vh[maxv],pre[maxv],preh[maxv]; void addEdge(int a,int b,int c) { e[edgeNum].v = b; e[edgeNum].flow = c; e[edgeNum].next = head[a]; head[a] = edgeNum++; e[edgeNum].v = a; e[edgeNum].flow = 0; e[edgeNum].next = head[b]; head[b] = edgeNum++; } void Init() { edgeNum = 0; memset(head,-1,sizeof(head)); memset(d,0,sizeof(d)); } int sap(int s,int t,int n) //源点,汇点,结点总数 { int i,x,y; int f,ans = 0; for(i = 0; i < n; i++) now[i] = head[i]; vh[0] = n; x = s; while(d[s] < n) { for(i = now[x]; i != -1; i = e[i].next) if(e[i].flow > 0 && d[y=e[i].v] + 1 == d[x]) break; if(i != -1) { now[x] = preh[y] = i; pre[y] = x; if((x=y) == t) { for(f = INF,i=t; i != s; i = pre[i]) if(e[preh[i]].flow < f) f = e[preh[i]].flow; ans += f; do { e[preh[x]].flow -= f; e[preh[x]^1].flow += f; x = pre[x]; }while(x!=s); } } else { if(!--vh[d[x]]) break; d[x] = n; for(i=now[x]=head[x]; i != -1; i = e[i].next) { if(e[i].flow > 0 && d[x] > d[e[i].v] + 1) { now[x] = i; d[x] = d[e[i].v] + 1; } } ++vh[d[x]]; if(x != s) x = pre[x]; } } return ans; } // int main() { int i,j,k; int cnt; while(scanf("%d",&n)!=EOF) { Init(); cnt = 0; for(i = 1; i <= n; i++) scanf("%d",&score[i]); for(i = 1; i <= n; i++) scanf("%d",&remain[i]); for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) scanf("%d",&match[i][j]); int high = score[1] + remain[1]; for(i = 2; i <= n; i++) //剪枝:1队剩下比赛全赢并且其余队全输,仍不能赢得比赛。 { if(score[i] > high) break; } if(i<=n) { printf("NO\n"); continue; } for(i = 2; i <= n; i++) for(j = i+1; j <= n; j++) if(match[i][j]) cnt++; int source = 0; int sink = cnt+n+1; int flow = 0; for(i = 2; i <= n; i++) addEdge(source,i,high-score[i]); k = 0; for(i = 2; i <= n; i++) { for(j = i+1; j <= n; j++) { if(match[i][j]) { ++k; addEdge(i,n+k,INF); addEdge(j,n+k,INF); addEdge(n+k,sink,match[i][j]); flow += match[i][j]; } } } if(flow == sap(source,sink,sink+1)) printf("YES\n"); else printf("NO\n"); } return 0; }