zoj 3348 网络流 汇总

来源:互联网 发布:中文文本词性标注算法 编辑:程序博客网 时间:2024/05/27 14:13

摘自:http://blog.csdn.net/abcjennifer/article/details/5896583

          http://blog.csdn.net/xsbailong/article/details/6975141 

          http://blog.csdn.net/y251673671/article/details/5841575

还没看明白,先存着


1.

Schedule


Time Limit: 1 Second      Memory Limit: 32768 KB

DD enters the top-level pingpong match! As the game going on, he wonders if there is possibility for him to get the champion(excluding joint champions). Now provide you the results of previous matches, and the details of rest matches. You have to judge it.

Input

There are several test cases. You should process to the end of input.

For each test case. the first line contain two integers n (1 <= n <= 50) and m(0 <= m <= 1000), n indicates the number of players. m indicates the matches have ended.

Then for the following m lines, each line has the format: "Player1 Player2 Result". Player1 and Player2 are the names of the two players, and Result could only be "win" or "lose".

In the next line there is another integer p (0 <= p <= 5000), indicates the matches that will start later.

Then for the following p lines, each line has the format: "Player1 Player2", which means Player1 and Player2 will start a match.

We ensure there is a player named "DD". The length of each player name is no longer than 10.

Output

You should only output "Yes" or "No" for each case, which means if DD has the possibility to be the champion.

Sample Input

3 2DD winsty winDD owen lose2winsty owenDD owen3 3DD winsty winDD owen losewinsty owen lose2owen winstyowen DD

Sample Output

YesNo

很经典的构图,最大流!
先统计每个人已经赢的次数和假设比赛他赢的次数,记c[i]
S连到每个赛手,流量为C[i]
……

int Getid(string name){int i,j;for(i=1;i<=alr;i++){if(name==person[i])return i;}//cannot find such nameperson[++alr]=name;return alr;}void init(){memset(p, -1, sizeof(p));memset(champ,0,sizeof(champ));memset(c,0,sizeof(c));ans = indexx = 0;int i,j,p1,p2,dd=0;string na1,na2,status;alr=0;for(i=0;i<temp;i++){cin>>na1>>na2>>status;if(status[0]=='w'){if(na1=="DD")dd++;else{p1=Getid(na1);c[p1]++;ans++;}}else{if(na2=="DD")dd++;else{p2=Getid(na2);c[p2]++;ans++;}}}cin>>temp;for(i=0;i<temp;i++){cin>>na1>>na2;//suppose first people winif(na1=="DD"||na2=="DD"){dd++;continue;}p1=Getid(na1);p2=Getid(na2);c[p1]++;champ[p1][p2]++;ans++;}for(i=1;i<=n;i++){add(0,i,c[i]);add(i,n+1,dd-1);for(j=1;j<=n;j++)add(i,j,champ[i][j]);}}
2.
/*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;}

3.
//一点思路都没有 看了别人的解题报告 
//我们贪心的让未来DD参加的比赛都赢,且我们可以知道未来每个人最多可以赢几场
//我们预先统计处当前每个点赢了多少场,点对之间还有多少次较量,在后来给出的比赛中我们人为指定一个人赢,并统计该点对场次 
//当然DD必须赢,增加源汇点,源到每个点代表了该点赢的次数,点到汇表示该点赢的场次不能超过DD(相等也不行)
//对于每个点对,连一条容量为未来比赛场次的边,那么跑一边最大流表示的是一组可行解当且仅当满流 
//可以这么理解中间那些场次的边的如果流经,那么表示原来的结果翻转了,原先赢的那个点不能再赢了,让给对应另个点赢
//还有一种按比赛和球员来建图的,把和DD有关的都去除,都算其赢,然后从源向每个比赛连容量为1,每个比赛向两个队员连容量为1
//每个队员向汇点连容量为w[DD]-w[i]-1,表示最多还能赢的场次,若最大流==比赛个数,则表示可以安排,反之不能,这种方法
//更直观,但是效率肯定没前一种快 
#include<iostream>#include<cstdio>#include<queue>#include<string>#include<map>using namespace std;const int V = 55;const int E = 10010;struct Edge{ int u,v,c; int next;}edg[E];int head[V],dis[V],w[V],a[V][V],stack[V],temp[V];int n,m,src,End,cnt,e;map<string,int>hash;inline void addedge(int u,int v,int c){ edg[e].u = u; edg[e].v = v; edg[e].c = c; edg[e].next = head[u]; head[u] = e++;}inline void Insert(int u,int v,int c){ addedge(u,v,c); addedge(v,u,0);}inline int gethash(char str[]){ if(hash.find(str) == hash.end())hash[str] = cnt++; return hash[str];}int bfs(){ queue<int>q; int now,i,v,c; fill(dis,dis + n + 1,-1); dis[0] = 0; q.push(0); while(!q.empty()) {  now  = q.front();  q.pop();  for(i = head[now]; i != -1; i = edg[i].next)  {   v = edg[i].v;   if(dis[v] == -1 && edg[i].c > 0)   {    dis[v] = dis[now] + 1;    q.push(v);   }  } } return (dis[End] >= 0); }int dinic(){ int ret = 0,top,t,u,v,i,min; while(bfs()) {  memcpy(temp,head,sizeof(temp));  top = 0;u = src;  while(1)  {   if(u == End)   {    t = INT_MAX;min = -1;    for(i = 0; i < top; i++)    {     v = stack[i];     if(t > edg[v].c)     {      t = edg[v].c;      min = i;     }    }    ret += t;    for(i = 0; i < top; i++)    {     v = stack[i];     edg[v].c -= t;     edg[v^1].c += t;    }    top = min;    u = edg[stack[top]].u;   }   for(i = temp[u]; i != -1; i = edg[i].next)   {    v = edg[i].v;    if(dis[v] == dis[u] + 1 && edg[i].c > 0)    break;   }   if(i != -1)   {    stack[top++] = i;    temp[u] = i;    u = edg[i].v;   }   else   {    if(top <= 0)break;    top--;    dis[u] = -1;    u = edg[stack[top]].u;   }  } } return ret;}void solve(){ int s,t,i,p,j; int sum; char s1[21],s2[21],s3[21],dd[] = "DD"; while(scanf("%d %d",&n,&m) != EOF) {  memset(head,-1,sizeof(head)); //这里居然写成sizeof(-1)调试了一个小时  错误方向找错了 晕 太粗心了   memset(w,0,sizeof(w));memset(a,0,sizeof(a));  e = src = cnt = sum = 0;End = n;   hash.clear();  gethash(dd);  for(i = 1; i <= m; i++)  {   scanf("%s %s %s",s1,s2,s3);   s = gethash(s1);   t = gethash(s2);   if(s3[0] == 'w')   w[s]++;   else   w[t]++;  }  scanf("%d",&p);  for(i = 1; i <= p; i++)  {   scanf("%s %s",s1,s2);   s = gethash(s1);   t = gethash(s2);   if(s > t)swap(s,t);   w[s]++;a[s][t]++;  }  if(w[0] == 0)  {   puts((n == 1) ? "Yes" : "No");   continue;  }  for(i = 1; i < n; i++)  {   sum += w[i];   Insert(src,i,w[i]);   Insert(i,End,w[0] - 1);   for(j = i + 1; j < n; j++)   {    if(a[i][j])Insert(i,j,a[i][j]);   }  }  puts((dinic() == sum) ? "Yes" : "No"); }}int main(){ solve(); return 0;}


原创粉丝点击