bzoj 5120 无限之环 & 洛谷 P4003 —— 费用流(多路增广SPFA)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120
https://www.luogu.org/problemnew/show/P4003
神奇的费用流建图;
首先,网格图,相邻之间有关系,所以先二分染色一下;
然后发现问题就是染色后黑白点之间要完美匹配插头;
所以可以考虑把旋转通过带一些代价变成插头方向的变化;
把一个格子拆成上下左右四个点,分类讨论,连边即可;
然而一开始连边竟然写了100多行...然后T了;
看了看题解代码,突然悟到了一些压行的方式,于是把建图压成了40行;
但还是T了,于是又改来改去,还把题解的建图粘过来囧,然后WA了;
这250行左右的代码就是一下午的辛酸调试:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int const xn=1e5,xm=4e5,inf=1e9;//
int n,m,hd[xn],ct=,to[xm],nxt[xm],w[xn],c[xn];
int dis[xn],pre[xn],inc[xn],S,T;
bool vis[xn];
queue<int>q;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
int Min(int x,int y){return x<y?x:y;}
void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,);}
int d[],bin[];
int id(int x,int y,int k){return ((x-)*m+y-)*+k;}
inline int get(int x)
{
int ret=;
for(int i=;i<=;i++)
if(x&bin[i-])d[i]=,ret++; else d[i]=;
return ret;
}
inline int op(int x){if(x<=)return x+; return x-;}
inline bool ck(){return (d[]&&d[]&&!d[]&&!d[])||(d[]&&d[]&&!d[]&&!d[]);}
inline bool bfs()
{
for(int i=S;i<=T;i++)vis[i]=;
for(int i=S;i<=T;i++)dis[i]=inf;
dis[S]=; inc[S]=inf; vis[S]=; q.push(S);
while(q.size())
{
int x=q.front(); q.pop(); vis[x]=;
//printf("x=%d d=%d\n",x,dis[x]);
for(int i=hd[x],u;i;i=nxt[i])
if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
{
//if(w[i]<0)printf("i=%d w=%d c=%d\n",i,w[i],c[i]);
dis[u]=dis[x]+w[i]; pre[u]=i;
inc[u]=Min(inc[x],c[i]);
if(!vis[u])vis[u]=,q.push(u);
}
}
//printf("dis[%d]=%d\n",T,dis[T]);
return dis[T]!=inf;
}
inline void up()
{
int x=T;
while(x!=S)
{
int i=pre[x];
c[i]-=inc[T]; c[i^]+=inc[T];
x=to[i^];
}
}
int main()
{
n=rd(); m=rd(); S=; T=n*m*+;
bin[]=; for(int i=;i<=;i++)bin[i]=(bin[i-]<<);
int goal=;
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
{
int x=rd(),u=; int t=(((i+j)&)==);
for (int k=;k<=;k++)
{
int nw=id(i,j,k);
if (x&(<<(k-))) u++,t?add(S,nw,,):add(nw,T,,);
}
goal+=u;
if (x==||x==) continue;
if (u==||u==) for (int k=;k<=;k++) add(id(i,j,k),id(i,j,k+&),,),add(id(i,j,k+&),id(i,j,k),,);
if (u==)
{
add(id(i,j,),id(i,j,),,),add(id(i,j,),id(i,j,),,);
add(id(i,j,),id(i,j,),,),add(id(i,j,),id(i,j,),,);
}
}
if (goal&) {puts("-1");return ;}goal>>=;
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
if (((i+j)&)==)
{
if (i>) add(id(i,j,),id(i-,j,),,);
if (j<m) add(id(i,j,),id(i,j+,),,);
if (i<n) add(id(i,j,),id(i+,j,),,);
if (j>) add(id(i,j,),id(i,j-,),,);
}
/*
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int x=rd(); int cnt=get(x); goal+=cnt;
bool t=(((i+j)&1)==0);
if(t){for(int k=1;k<=4;k++)if(d[k])add(S,id(i,j,k),0,1);}
else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),T,0,1);}
if(x==5||x==10)continue;
if(cnt==1)
{
int nw; for(int k=1;k<=4;k++)if(d[k]){nw=k; break;}
if(t){for(int k=1;k<=4;k++)if(!d[k])add(nw,id(i,j,k),(k==op(nw)?2:1),1);}
else {for(int k=1;k<=4;k++)if(!d[k])add(id(i,j,k),nw,(k==op(nw)?2:1),1);}
}
if(cnt==3)
{
int nw; for(int k=1;k<=4;k++)if(!d[k]){nw=k; break;}
if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),nw,(k==op(nw)?2:1),1);}
else {for(int k=1;k<=4;k++)if(d[k])add(nw,id(i,j,k),(k==op(nw)?2:1),1);}
}
if(cnt==2)
{
if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),id(i,j,op(k)),1,1);}
else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,op(k)),id(i,j,k),1,1);}
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(((i+j)&1)==0)
{
if(i>1)add(id(i,j,1),id(i-1,j,3),0,1);
if(j<m)add(id(i,j,2),id(i,j+1,4),0,1);
if(i<n)add(id(i,j,3),id(i+1,j,1),0,1);
if(j>1)add(id(i,j,4),id(i,j-1,2),0,1);
}
*/
/*
if(((i+j)&1)==0)//black
{
if(!ck())//!straight
{
if(cnt==1)
{
for(int k=1;k<=4;k++)
if(d[k])
{
int nw=id(i,j,k); add(S,nw,0,1);
for(int l=1;l<=4;l++)
if(l==k)continue;
else if(l==op(k))add(nw,id(i,j,l),2,1);
else add(nw,id(i,j,l),1,1);
break;
}
}
else if(cnt==2)
{
for(int k=1;k<=4;k++)
if(d[k])
{
add(S,id(i,j,k),0,1);
add(id(i,j,k),id(i,j,op(k)),1,1);
}
}
else if(cnt==3)
{
for(int k=1;k<=4;k++)
if(d[k])add(S,id(i,j,k),0,1);
else
{
for(int l=1,nw=id(i,j,k);l<=4;l++)
if(l==k)continue;
else if(l==op(k))add(id(i,j,l),nw,2,1);
else add(id(i,j,l),nw,1,1);
}
}
else if(cnt==4)
for(int k=1;k<=4;k++)add(S,id(i,j,k),0,1);
if(i-1)add(id(i,j,1),id(i-1,j,3),0,1);
if(j<m)add(id(i,j,2),id(i,j+1,4),0,1);
if(i<n)add(id(i,j,3),id(i+1,j,1),0,1);
if(j-1)add(id(i,j,4),id(i,j-1,2),0,1);
}
else//straight
{
for(int k=1;k<=4;k++)
if(d[k])add(S,id(i,j,k),0,1);
if(i-1&&d[1])add(id(i,j,1),id(i-1,j,3),0,1);
if(j<m&&d[2])add(id(i,j,2),id(i,j+1,4),0,1);
if(i<n&&d[3])add(id(i,j,3),id(i+1,j,1),0,1);
if(j-1&&d[4])add(id(i,j,4),id(i,j-1,2),0,1);
}
}
else//white
{
if(!ck())//!straight
{
if(cnt==1)
{
for(int k=1;k<=4;k++)
if(d[k])
{
int nw=id(i,j,k); add(nw,T,0,1);
for(int l=1;l<=4;l++)
if(l==k)continue;
else if(l==op(k))add(id(i,j,l),nw,2,1);
else add(id(i,j,l),nw,1,1);
break;
}
}
else if(cnt==2)
{
for(int k=1;k<=4;k++)
if(d[k])
{
add(id(i,j,k),T,0,1);
add(id(i,j,op(k)),id(i,j,k),1,1);
}
}
else if(cnt==3)
{
for(int k=1;k<=4;k++)
if(d[k])add(id(i,j,k),T,0,1);
else
{
int nw=id(i,j,k);
for(int l=1;l<=4;l++)
if(l==k)continue;
else if(l==op(k))add(nw,id(i,j,l),2,1);
else add(nw,id(i,j,l),1,1);
}
}
else if(cnt==4)
for(int k=1;k<=4;k++)add(id(i,j,k),T,0,1);
}
else//straight
{
for(int k=1;k<=4;k++)
if(d[k])add(id(i,j,k),T,0,1);
}
}
}
*/
//if(goal&1){puts("-1"); return 0;} goal>>=1;
int ans=,flow=;
while(bfs())ans+=dis[T]*inc[T],flow+=inc[T],up();
if(flow==goal)printf("%d\n",ans);
else puts("-1");
return ;
}
囧
突然发现连边里 nw 和 id(i,j,nw) 不分了,改过来后竟然——就A了!
但是空间必须开 4e5?明明我一开始算的应该是 4e4 ...
然而这篇代码在洛谷上一交,全是TLE...似乎还应该写多路增广SPFA...
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int const xn=1e5,xm=4e5,inf=1e9;
int n,m,hd[xn],ct=,to[xm],nxt[xm],w[xn],c[xn];
int dis[xn],pre[xn],inc[xn],S,T;
bool vis[xn];
queue<int>q;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
int Min(int x,int y){return x<y?x:y;}
void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,);}
int d[],bin[];
int id(int x,int y,int k){return ((x-)*m+y-)*+k;}
inline int get(int x)
{
int ret=;
for(int i=;i<=;i++)
if(x&bin[i-])d[i]=,ret++; else d[i]=;
return ret;
}
inline int op(int x){if(x<=)return x+; return x-;}
inline bool ck(){return (d[]&&d[]&&!d[]&&!d[])||(d[]&&d[]&&!d[]&&!d[]);}
inline bool bfs()
{
for(int i=S;i<=T;i++)dis[i]=inf;
dis[S]=; inc[S]=inf; vis[S]=; q.push(S);
while(q.size())
{
int x=q.front(); q.pop(); vis[x]=;
for(int i=hd[x],u;i;i=nxt[i])
if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
{
dis[u]=dis[x]+w[i]; pre[u]=i;
inc[u]=Min(inc[x],c[i]);
if(!vis[u])vis[u]=,q.push(u);
}
}
return dis[T]!=inf;
}
inline void up()
{
int x=T;
while(x!=S)
{
int i=pre[x];
c[i]-=inc[T]; c[i^]+=inc[T];
x=to[i^];
}
}
int main()
{
n=rd(); m=rd(); S=; T=n*m*+;
bin[]=; for(int i=;i<=;i++)bin[i]=(bin[i-]<<);
int goal=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
int x=rd(); int cnt=get(x); goal+=cnt;
bool t=(((i+j)&)==);
if(t){for(int k=;k<=;k++)if(d[k])add(S,id(i,j,k),,);}
else {for(int k=;k<=;k++)if(d[k])add(id(i,j,k),T,,);}
if(x==||x==)continue;
if(cnt==)
{
int nw; for(int k=;k<=;k++)if(d[k]){nw=k; break;}
if(t){for(int k=;k<=;k++)if(!d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?:),);}//id(i,j,nw)!!
else {for(int k=;k<=;k++)if(!d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?:),);}
}
if(cnt==)
{
int nw; for(int k=;k<=;k++)if(!d[k]){nw=k; break;}
if(t){for(int k=;k<=;k++)if(d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?:),);}
else {for(int k=;k<=;k++)if(d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?:),);}
}
if(cnt==)
{
if(t){for(int k=;k<=;k++)if(d[k])add(id(i,j,k),id(i,j,op(k)),,);}
else {for(int k=;k<=;k++)if(d[k])add(id(i,j,op(k)),id(i,j,k),,);}
}
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(((i+j)&)==)
{
if(i>)add(id(i,j,),id(i-,j,),,);
if(j<m)add(id(i,j,),id(i,j+,),,);
if(i<n)add(id(i,j,),id(i+,j,),,);
if(j>)add(id(i,j,),id(i,j-,),,);
}
if(goal&){puts("-1"); return ;} goal>>=;
int ans=,flow=;
while(bfs())ans+=dis[T]*inc[T],flow+=inc[T],up();
if(flow==goal)printf("%d\n",ans);
else puts("-1");
return ;
}
单路增广SPFA
于是学习了一下多路增广SPFA的写法,似乎就是用SPFA跑出一些可行路线,然后 dinic,据说在边多流量少的图上作用很好;
竟然一下子跑得飞快,吓了一跳!这时间根本不是一个层次的啊!实在是妙!
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int const xn=1e5,xm=4e5,inf=1e9;
int n,m,hd[xn],ct=,to[xm],nxt[xm],w[xn],c[xn];
int dis[xn],pre[xn],S,T,cur[xn],ans;
bool vis[xn];
queue<int>q;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
int Min(int x,int y){return x<y?x:y;}
void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,);}
int d[],bin[];
int id(int x,int y,int k){return ((x-)*m+y-)*+k;}
inline int get(int x)
{
int ret=;
for(int i=;i<=;i++)
if(x&bin[i-])d[i]=,ret++; else d[i]=;
return ret;
}
inline int op(int x){if(x<=)return x+; return x-;}
inline bool ck(){return (d[]&&d[]&&!d[]&&!d[])||(d[]&&d[]&&!d[]&&!d[]);}
bool SPFA()
{
for(int i=S;i<=T;i++)vis[i]=;
for(int i=S;i<=T;i++)dis[i]=inf;
dis[S]=, q.push(S);
while(q.size())
{
int x=q.front(); q.pop(), vis[x]=;
for(int i=hd[x],u;i;i=nxt[i])
if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
{
dis[u]=dis[x]+w[i];
if(!vis[u])vis[u]=,q.push(u);
}
}
return dis[T]!=inf;
}
int dfs(int x,int fl)
{
if(x==T)return fl;
vis[x]=;
for(int &i=cur[x],u,tmp;i;i=nxt[i])
if(!vis[u=to[i]]&&c[i]&&dis[u]==dis[x]+w[i])
if(tmp=dfs(u,Min(fl,c[i])))
{c[i]-=tmp,c[i^]+=tmp,ans+=w[i]*tmp; return tmp;}
return ;
}
int MCMF()
{
int ret=;
while(SPFA())
{
for(int i=S;i<=T;i++)cur[i]=hd[i];
int tmp;
while(tmp=dfs(S,inf))ret+=tmp;
}
return ret;
}
int main()
{
n=rd(); m=rd(); S=; T=n*m*+;
bin[]=; for(int i=;i<=;i++)bin[i]=(bin[i-]<<);
int goal=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
int x=rd(); int cnt=get(x); goal+=cnt;
bool t=(((i+j)&)==);
if(t){for(int k=;k<=;k++)if(d[k])add(S,id(i,j,k),,);}
else {for(int k=;k<=;k++)if(d[k])add(id(i,j,k),T,,);}
if(x==||x==)continue;
if(cnt==)
{
int nw; for(int k=;k<=;k++)if(d[k]){nw=k; break;}
if(t){for(int k=;k<=;k++)if(!d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?:),);}//id(i,j,nw)!!
else {for(int k=;k<=;k++)if(!d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?:),);}
}
if(cnt==)
{
int nw; for(int k=;k<=;k++)if(!d[k]){nw=k; break;}
if(t){for(int k=;k<=;k++)if(d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?:),);}
else {for(int k=;k<=;k++)if(d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?:),);}
}
if(cnt==)
{
if(t){for(int k=;k<=;k++)if(d[k])add(id(i,j,k),id(i,j,op(k)),,);}
else {for(int k=;k<=;k++)if(d[k])add(id(i,j,op(k)),id(i,j,k),,);}
}
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(((i+j)&)==)
{
if(i>)add(id(i,j,),id(i-,j,),,);
if(j<m)add(id(i,j,),id(i,j+,),,);
if(i<n)add(id(i,j,),id(i+,j,),,);
if(j>)add(id(i,j,),id(i,j-,),,);
}
if(goal&){puts("-1"); return ;} goal>>=;
int flow=MCMF();
if(flow==goal)printf("%d\n",ans);
else puts("-1");
return ;
}
bzoj 5120 无限之环 & 洛谷 P4003 —— 费用流(多路增广SPFA)的更多相关文章
- 落谷p3376 最大流EdmondsKarp增广路模板
参考: https://blog.csdn.net/txl199106/article/details/64441994 分析: 该算法是用bfs求出是否有路从s到t, 然后建立反向边(关于反向边), ...
- 草地排水 洛谷P2740 最大流 入门题目
草地排水 洛谷P2740 最大流入门题目 题意 在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水.这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间.因此,农夫约翰修建了一 ...
- LOJ 2979 「THUSCH 2017」换桌——多路增广费用流
题目:https://loj.ac/problem/2979 原来的思路: 优化连边.一看就是同一个桌子相邻座位之间连边.相邻桌子对应座位之间连边. 每个座位向它所属的桌子连边.然后每个人建一个点,向 ...
- 洛谷P4003 无限之环(infinityloop)(网络流,费用流)
洛谷题目传送门 题目 题目描述 曾经有一款流行的游戏,叫做 Infinity Loop,先来简单的介绍一下这个游戏: 游戏在一个 n ∗ m 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在格 ...
- 洛谷P4003 无限之环(费用流)
传送门 神仙题啊……不看题解我可能一年都不一定做得出来……FlashHu大佬太强啦 到底是得有怎样的脑回路才能一眼看去就是费用流啊…… 建好图之后套个板子就好了,那么我们着重来讨论一下怎么建图 首先, ...
- 洛谷P4003 [国家集训队2017]无限之环 网络流 最小费用最大流
题意简述 有一个\(n\times m\)棋盘,棋盘上每个格子上有一个水管.水管共有\(16\)种,用一个\(4\)位二进制数来表示当前水管向上.右.下.左有个接口.你可以旋转除了\((0101)_2 ...
- LOJ 2321 清华集训2017 无限之环 拆点+最小费用最大流
题面:中文题面,这里不占用篇幅 分析: 看到题面,我就想弃疗…… 但是作为任务题单,还是抄了题解…… 大概就是将每个格子拆点,拆成五个点,上下左右的触点和一个负责连源汇点的点(以下简称本点). 这个这 ...
- 【题解】洛谷P1073 [NOIP2009TG] 最优贸易(SPFA+分层图)
次元传送门:洛谷P1073 思路 一开始看题目嗅出了强连通分量的气息 但是嫌长没打 听机房做过的dalao说可以用分层图 从来没用过 就参考题解了解一下 因为每个城市可以走好几次 所以说我们可以在图上 ...
- 洛谷P1462 通往奥格瑞玛的道路(二分+spfa,二分+Dijkstra)
洛谷P1462 通往奥格瑞玛的道路 二分费用. 用血量花费建图,用单源最短路判断 \(1\) 到 \(n\) 的最短路花费是否小于 \(b\) .二分时需要不断记录合法的 \(mid\) 值. 这里建 ...
随机推荐
- Image Recognition
https://www.tensorflow.org/tutorials/image_recognition
- Django 视图之CBV
CBV 所谓的CBV(class base view) 在视图里面,用类的方式来写逻辑 那么对于FBV,CBV有什么优势? CBV(class base views) 就是在视图里使用类处理请求. P ...
- 我的Android进阶之旅------>Android二级ListView列表的实现
实现如下图所示的二级列表效果 首先是在布局文件中,布局两个ListView,代码如下: <LinearLayout xmlns:android="http://schemas.andr ...
- 怎样将lua移植到arm平台的linux内核
将脚本移植到内核是一件非常酷的事情,lua已经被移植到NetBSD的内核中,也有一个叫lunatik的项目把lua移植到了linux内核.仅仅可惜仅仅支持x86.不支持arm,在网上搜索了下,没有找到 ...
- ABAP服务器文件操作
转自http://blog.itpub.net/547380/viewspace-876667/ 在程序设计开发过程中,很多要对文件进行操作,这又分为对本地文件操作和服务器文件操作.对本地文件操作使用 ...
- C# ADO.NET学习
Connetction 对象: 数据库服务器 数据库名字 登录名.密码 连接数据库所需要的其他参数 Command对象: ExecuteScalar();//首行首列的内容 ExecuteNomQue ...
- Python基础(5)_字符编码、文件处理
一.文件读取过程: 1. 文本编辑器存取文件的原理(nodepad++,pycharm,word) 打开编辑器就打开了启动了一个进程,是在内存中的,所以在编辑器编写的内容也都是存放与内存中的,断电后数 ...
- (转载)C #开源框架
Json.NET http://json.codeplex.com/ Json.Net 是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单.通过Li ...
- Python常用转换函数
字符串转换为整数 int() 如int('2'). 字符串转换为浮点数 float() 如float('12.34') ASCII码转换为字符 chr() 如chr(97) 字符转换为ASCII码 o ...
- 收缩VC数据库
注意: 在收缩日志前必须截断事务日志. 一. SQL Server 2008 收缩日志 (1) 使用SQL管理器收缩日志 第一步执行如下命令 ALTER DATABASE dbname SET REC ...