zoj 3348
2011-11-15 22:56
225 查看
纠结了好久,终于过了
/* zoj_3348 最大流 经典的构图! 转某acmer构图解释: 构图方法一:点规模(n+m) 比较常规的方法: 将n个人看成点,m场比赛看成点,设源source,汇sink,对每场比赛,从source引边容量为1, 一场比赛可以被两人中的任何一个赢得,所以从每场比赛引容量为1的边到对应的两个选手, 假设为v,u,由于source到这一场比赛只有一个流量,则保证了这场比赛只能由v,u中间的某 一个人瓜分得到,每个人引边到sink,容量为假设值,二分这个值,判断是否能将source给每 场比赛的流量流尽而决定此安排是否可行,可行则缩小这个值,否则放大. 构图方法二:点规模(n) 更快 我们贪心的让未来DD参加的比赛都赢,且我们可以知道未来每个人最多可以赢几场给所有选 手编号,预先统计出所有选手已经胜利的场次,然后对所有还没有进行的比赛,假设其中任 意一个选手获胜,并统计胜负关系,(加入每个人胜利的次数在w[]中,每两个选手互相对战 胜利的次数在a[][]中)即某两个选手a,b进行比赛a赢b的次数和b赢a的次数,这里要注意的 是两个选手进行比赛,你假设任意一个选手获胜都是正确的,待会你会发现其实假设仅仅只是 假设。增加超级源超级汇,超级源向每一个结点射出一条容量是这个点胜利场次的边,所有 的点向汇点连一条容量是w[ID[DD]]-1的边,显然是为了限制每个点的胜利次数不能大于等于 DD.中间任意两个结点根据胜负关系建立一条容量是a[i][j]的边,跑一遍最大流即可,如果满 流,说明有可行解,否则无解。 下面来分析一下构图的原理,超级源向所有选手连一条容量是他将要获胜场次的边,比如说是c, 说明这个选手有c场胜利要分配,如果这条边上的流量直接流向了汇点,说明该选手获胜,如果 流量通过有向边流向了他的对手,说明这个胜利果实被他的对手拿走了,也就是实际上输掉了 比赛,所以我才说假设仅仅是假设。再加上有每个点到汇点的限流,跑一遍最大流如果能满流 说明比赛可以合理的分配胜负关系使得每个人的胜利场次都不超过DD,如果不能,无解。 Process: 1.用构图方法二做的。由于要确定已经比赛的结果中是否有人已经胜利足够多场,所以统计了一 个ori[]做了处理,具体见代码。归根到底就是方法二的思想。然后wa了。。 2.发现很严重的问题是,因为已经贪心的认为有DD的比赛DD都胜利了,但是我在构图中并没有去 掉与DD有关的边。果断去掉,依旧wa。。 3.终于发现n=0的情况我没特判,特判以后终于过了。真的要多多注意边界问题~ */ #include <iostream> #include <cstdio> #include <iomanip> #include <string.h> #include <limits.h> #include <queue> #include <map> using namespace std; map <string,int>mp; int ori[55],visit[55],pre[55]; //ori统计已经举行的比赛各选手胜的场次 int w[55][55]; int DDwin; bool check( int n ) { int i; for( i=1;i<=n;i++ ) if( w[i][n+1]<0 ) break; if( i==n+1 ) return true; else return false; } bool bfs( int n ) { int i,temp; queue <int>q; memset( visit,0,sizeof(visit) ); q.push(0); visit[0]=1; while( !q.empty() ) { temp=q.front(); q.pop(); for( i=0;i<=n+1;i++ ) if( w[temp][i]>0 && !visit[i] ) { visit[i]=1; pre[i]=temp; if( i==n+1 ) return true; q.push(i); } } return false; } int EdmondsKarp( int n ) { int i,mini,flow; flow=0; while( bfs(n) ) { mini=INT_MAX; for( i=n+1;i!=0;i=pre[i] ) if( mini>w[ pre[i] ][i] ) mini=w[ pre[i] ][i]; for( i=n+1;i!=0;i=pre[i] ) { w[ pre[i] ][i]-=mini; w[i][ pre[i] ]+=mini; } flow+=mini; } return flow; } int main() { int n,m,p,i,j,k,sum; string a,b,v; while( cin>>n>>m ) { i=1; DDwin=0; memset( ori,0,sizeof(ori) ); memset( w,0,sizeof(w) ); for( j=0;j<m;j++ ) { cin>>a>>b>>v; if( mp.find(a)==mp.end() ) mp[a]=i++; if( mp.find(b)==mp.end() ) mp[b]=i++; if( (a=="DD" && v=="win") || (b=="DD" && v=="lose") ) DDwin++; if( v=="win" ) ori[ mp[a] ]++; else ori[ mp[b] ]++; } cin>>p; for( j=0;j<p;j++ ) { cin>>a>>b; if( mp.find(a)==mp.end() ) mp[a]=i++; if( mp.find(b)==mp.end() ) mp[b]=i++; if( a=="DD" || b=="DD" ) { DDwin++; //去掉与DD有关的边,否则wa //if( a=="DD" ) w[0][ mp[a] ]++ , w[ mp[a] ][ mp[b] ]++ ; //else w[0][ mp[b] ]++ , w[ mp[b] ][ mp[a] ]++ ; } else { w[0][ mp[a] ]++; w[ mp[a] ][ mp[b] ]++; } } sum=0; for( j=1;j<=n;j++ ) { if( j!=mp["DD"] ) w[j][n+1]=DDwin-ori[j]-1; //else w[j][n+1]=DDwin-ori[j]; //去掉与DD有关的边,否则wa sum+=w[0][j]; } if(n==1) printf("Yes\n"); //加特判,因为这wa了 else if( check(n) ) { if( sum==EdmondsKarp(n) ) printf( "Yes\n" ); else printf( "No\n" ); } else printf( "No\n" ); mp.clear(); } return 0; }
相关文章推荐
- Schedule zoj 3348
- ZOJ 3348 网络流最大流 解题报告
- ZOJ 3348 Schedule(map运用+网络流之最大流)(竞赛问题升级版)
- 【 zoj 3348 】Schedule 【网络流经典建图】
- zoj 3348
- zoj 3348 网络流 汇总
- zoj 3348 Schedule 【最大流经典建模】 【好题】
- ZOJ_1037_Gridland
- ZOJ 3532 Kevin Bacon 最短路
- ZOJ 3299-Fall the Brick(线段树+离散化)
- ZOJ - 3981 A.Balloon Robot 思维
- ZOJ_1067_Color Me Less
- ZOJ 3015 Collision Ball Game(反射)
- ZOJ Problem Set - 3543 Number String DP
- One Person Game ZOJ - 3329 期望dp
- zoj 3329 One Person Game 概率dp(有环)
- ZOJ-2939
- ZOJ 3717 Balloon(2-sat)
- ZOJ
- zoj - 1076 - Gene Assembly