背景:
本来好像今天继续做DP来着,某人把我拉来被网络流虐。。
其实感觉网络流考的不是这个算法,考的是你能想到这题是网络流,而我今天也做了(5)道题,因为知道是网络流的题,感觉就是把建边改改就A了 (虽然有一题De了一下午Bug
算了,废话不多说,讲题吧
又是一道文字题 (本蒟蒻看错题(5)次
这题其实不难(主要知道是网络流的题,
从源点向每一个(byx)的人连一条边,边的容量为这个人的寿命,然后从这个人向他可以打败的人连一条容量为(1)的边,因为每两个人只能比一场,最后从诗乃酱的人向汇点连一条容量为此人寿命的边。
注意,YYY是可以给J续寿命的,而且自己的寿命不会减少,所以就算这个YYY的寿命是(0),也可以给J续命。
然后?然后就跑最大流了,这个应该不用说了。。
手起,码落:
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define re register
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int N=21500;
struct edge{int v,net,val;}e[N];
int n,m,s,a[N],b[N],t,ans,s1,cnt=1,hd[N],num1,num2,dep[N],cur[N];
bool KO[10][10];
inline void add(int u,int v,int val)
{
e[++cnt].v=v,e[cnt].net=hd[u],e[cnt].val=val,hd[u]=cnt;
e[++cnt].v=u,e[cnt].net=hd[v],e[cnt].val=0,hd[v]=cnt;
}
string ch;
queue <int> q;
bool bfs()
{
for(;!q.empty();q.pop());
memset(dep,0,sizeof(dep));
for(re int i=0;i<=(n<<1)+2;i++)cur[i]=hd[i];
dep[s]=1,q.push(s);
for(re int u;!q.empty();)
{
u=q.front(),q.pop();
for(re int v,i=hd[u];i;i=e[i].net)
{
v=e[i].v;
if(e[i].val==0||dep[v])continue;
dep[v]=dep[u]+1;
if(v==t)return 1;
q.push(v);
}
}
return 0;
}
int dinic(int u,int flow)
{
if(u==t) return flow;
re int rest=flow;
for(re int i=cur[u],v,k;i;i=e[i].net)
{
v=e[i].v;cur[u]=i;
if(e[i].val==0||dep[v]!=dep[u]+1)continue;
k=dinic(v,min(e[i].val,rest));
e[i].val-=k,e[i^1].val+=k;
rest-=k;
}
return flow-rest;
}
int main()
{
KO[1][2]=KO[1][3]=KO[2][4]=KO[2][5]=KO[3][2]=KO[3][5]=KO[4][3]=KO[4][1]=KO[5][1]=KO[5][4]=1;
scanf("%d%d",&n,&m);
s1=(n<<1)+1,t=(n<<1)+2;
add(s,s1,m);
for(re int i=1;i<=n;i++)
{
cin>>ch;
if(ch=="J")a[i]=1;
else if(ch=="W")a[i]=2;
else if(ch=="HK")a[i]=3;
else if(ch=="YYY")a[i]=4,num1++;
else a[i]=5;
}
for(re int i=1;i<=n;i++)
{
cin>>ch;
if(ch=="J")b[i]=1;
else if(ch=="W")b[i]=2;
else if(ch=="HK")b[i]=3;
else if(ch=="YYY")b[i]=4,num2++;
else b[i]=5;
}
for(re int i=1,val;i<=n;i++)
{
scanf("%d",&val);
if(a[i]==1) add(s1,i,val+num1);
else add(s1,i,val);
for(re int j=1;j<=n;j++)
if(KO[a[i]][b[j]])add(i,j+n,1);
}
for(re int i=1,val;i<=n;i++)
{
scanf("%d",&val);
if(b[i]==1) add(i+n,t,val+num2);
else add(i+n,t,val);
}
for(;bfs();ans+=dinic(s,inf));
printf("%d",ans);
return 0;
}
这题虽然评分是省选,其实可以算最大流的模板题了,很适合刚学最大流的OIer做(至少比上一题简单
因为输入的是书与作业本,答案的可能匹配,所以在网络流分层时,可以吧书放在中间,然后再建开始边。
水的网络流题建边也差不多,先从汇点向作业本建容量为(1)的边,因为每一本作业本只能匹配一次,然后就是再向书,再向答案,最后向汇点建边。
不过要注意的是,因为每一本书只能匹配一次,所以要把书的节点拆成两个,中间建一条容量为(1)的边.
又没了,上代码吧(后面两题除了建边都一样就不copy代码了
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define re register
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int N=41005,M=140005;
struct edge{int v,net,val;}e[M];
int n1,n2,n3,m1,m2,s,t,ans,cnt=1,hd[N],dep[N],cur[N];
inline void add(int u,int v,int val)
{
e[++cnt].v=v,e[cnt].net=hd[u],e[cnt].val=val,hd[u]=cnt;
e[++cnt].v=u,e[cnt].net=hd[v],e[cnt].val=0,hd[v]=cnt;
}
string ch;
queue <int> q;
bool bfs()
{
for(;!q.empty();q.pop());
memset(dep,0,sizeof(dep));
for(re int i=0;i<=n1+n1+n2+n3+1;i++)cur[i]=hd[i];
dep[s]=1,q.push(s);
for(re int u;!q.empty();)
{
u=q.front(),q.pop();
for(re int v,i=hd[u];i;i=e[i].net)
{
v=e[i].v;
if(e[i].val==0||dep[v])continue;
dep[v]=dep[u]+1;
if(v==t)return 1;
q.push(v);
}
}
return 0;
}
int dinic(int u,int flow)
{
if(u==t) return flow;
re int rest=flow;
for(re int i=cur[u],v,k;i;i=e[i].net)
{
v=e[i].v;cur[u]=i;
if(e[i].val==0||dep[v]!=dep[u]+1)continue;
k=dinic(v,min(e[i].val,rest));
e[i].val-=k,e[i^1].val+=k;
rest-=k;
}
return flow-rest;
}
int main()
{
// freopen("P1231.in","r",stdin);
scanf("%d%d%d",&n1,&n2,&n3);
scanf("%d",&m1),t=n1+n2+n3+n1+1;
for(re int i=1;i<=n2;i++)add(s,i+n1,1);
for(re int i=1;i<=n3;i++)add(i+n1+n2,t,1);
for(re int i=1;i<=n1;i++)add(i,i+n1+n2+n3,1);
for(re int i=1,u,v;i<=m1;i++)
{
scanf("%d%d",&u,&v);
add(v+n1,u,1);
}
scanf("%d",&m2);
for(re int i=1,u,v;i<=m2;i++)
{
scanf("%d%d",&u,&v);
add(u+n1+n2+n3,v+n1+n2,1);
}
for(;bfs();ans+=dinic(s,inf));
printf("%d",ans);
return 0;
}
P2598 [ZJOI2009]狼和羊的故事
这题需要一点思考,而且不会一眼看出来是一道网络流。
先把地图转化一下,变成一个有(n imes m)个节点的网络图,每个节点向四周连边,而我们的答案该怎么求?用围栏可以看成把这条边割掉,而答案就是求最小割的大小。
等等,最小割?
我们好像刚学最大流是就知道了,最大流等于最小割,所以把羊或者狼的点连上源点,另外一个连上汇点,跑最大流就好了,不是任何动物的领地的点不用管。
然后,我们又快乐的A掉了一题!
P2472 [SCOI2007]蜥蜴
这题又比较基础了,从每个点向它能到达的点连边,然后从源点向有蜥蜴的石柱连边,最后那些石柱可以一步跳出地图,就把它们连向汇点。
但这题也要拆点,把每个点拆成两个,中间连一条容量为石柱高度的边,表示最多能通过多少条蜥蜴,然后跑最大流了。
好像都挺水的哩