题目地址:ZOJ 3348
仍然是一道竞赛问题的网络流问题,但是这道题再用上次的竞赛建图方法就不行了,5000场比赛,明显会超时,于是需要换种建图思路了。上一道经典竞赛问题戳这里
上一道的胜负转换是利用专门给比赛建一个点,通过对比赛双方的流向来控制胜负关系,这里的建图方法更加巧妙(膜拜想出这个方法的大牛。。。),是先假设其中一方获胜,用mp[a][b]来表示a赢b的次数,将a与b连边,权值为mp[a][b],这样的话,前面的假设就仅仅只是假设而已,因为在这里,如果a的流量流向了b,说明a的胜利果实到了b,相当于b获胜。这是题解上的说法,但是我个人是理解的是在这里把比赛转换成了边(上一道是点),这条边连接比赛双方,然后这条边上的流量通过某一方从源点里加入都行,最后看从哪个端点流向了汇点就是相当于哪边赢,这是我个人的理解。
建图方法就是建一源点与汇点,每个人都与源点连边,权值为这个人将要获胜的场数,与汇点连边,权值为与DD的积分差-1,很明显是用来保证积分不超过DD的,然后将假设的胜负关系用有向边将各点连接起来。最后求一次最大流判断是否满流。对于每个人的转换处理,我是直接用的map映射,这样比较方便。
代码如下;
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int head[200], source, sink, nv, cnt;
int cur[200], num[200], d[200], pre[200], q[200], w[200], mp[200][200];
struct node
{
int u, v, cap, next;
} edge[1000000];
void add(int u, int v, int cap)
{
edge[cnt].v=v;
edge[cnt].cap=cap;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].v=u;
edge[cnt].cap=0;
edge[cnt].next=head[v];
head[v]=cnt++;
}
void bfs()
{
memset(num,0,sizeof(num));
memset(d,-1,sizeof(d));
int f1=0, f2=0, i;
d[sink]=0;
num[0]=1;
q[f1++]=sink;
while(f1>=f2)
{
int u=q[f2++];
for(i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(d[v]==-1)
{
d[v]=d[u]+1;
num[d[v]]++;
q[f1++]=v;
}
}
}
}
int isap()
{
memcpy(cur,head,sizeof(cur));
bfs();
int flow=0, u=pre[source]=source, i;
while(d[source]<nv)
{
if(u==sink)
{
int f=INF,pos;
for(i=source; i!=sink; i=edge[cur[i]].v)
{
if(f>edge[cur[i]].cap)
{
f=edge[cur[i]].cap;
pos=i;
}
}
for(i=source; i!=sink; i=edge[cur[i]].v)
{
edge[cur[i]].cap-=f;
edge[cur[i]^1].cap+=f;
}
flow+=f;
u=pos;
}
for(i=cur[u]; i!=-1; i=edge[i].next)
{
if(d[edge[i].v]+1==d[u]&&edge[i].cap)
break;
}
if(i!=-1)
{
cur[u]=i;
pre[edge[i].v]=u;
u=edge[i].v;
}
else
{
if(--num[d[u]]==0) break;
int mind=nv;
for(i=head[u]; i!=-1; i=edge[i].next)
{
if(mind>d[edge[i].v]&&edge[i].cap)
{
mind=d[edge[i].v];
cur[u]=i;
}
}
d[u]=mind+1;
num[d[u]]++;
u=pre[u];
}
}
return flow;
}
int main()
{
int n, m, p, i, j, z;
char s1[20], s2[20], s3[20];
map<string,int>Q;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(w,0,sizeof(w));
memset(head,-1,sizeof(head));
cnt=0;
z=1;
Q.clear();
char s4[20]="DD";
Q[s4]=1;
for(i=0; i<m; i++)
{
scanf("%s%s%s",s1,s2,s3);
if(!Q[s1])
Q[s1]=++z;
if(!Q[s2])
Q[s2]=++z;
if(strcmp(s3,"win")==0)
{
w[Q[s1]]++;
}
else
{
w[Q[s2]]++;
}
}
scanf("%d",&p);
memset(mp,0,sizeof(mp));
for(i=0; i<p; i++)
{
scanf("%s%s",s1,s2);
if(!Q[s1])
Q[s1]=++z;
if(!Q[s2])
Q[s2]=++z;
if(Q[s1]==1||Q[s2]==1)
{
w[1]++;
}
else
{
mp[Q[s1]][Q[s2]]++;
w[Q[s1]]++;
}
}
int sum=0;
for(i=2;i<=z;i++)
sum+=w[i];
source=0;
sink=z;
nv=sink+1;
for(i=2; i<=z; i++)
{
add(source,i-1,w[i]);
add(i-1,sink,w[1]-1);
for(j=2; j<=z; j++)
{
add(i-1,j-1,mp[i][j]);
}
}
int x=isap();
//printf("%d\n",x);
if(x==sum)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}