题目: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)的更多相关文章

  1. 落谷p3376 最大流EdmondsKarp增广路模板

    参考: https://blog.csdn.net/txl199106/article/details/64441994 分析: 该算法是用bfs求出是否有路从s到t, 然后建立反向边(关于反向边), ...

  2. 草地排水 洛谷P2740 最大流 入门题目

    草地排水 洛谷P2740 最大流入门题目 题意 在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水.这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间.因此,农夫约翰修建了一 ...

  3. LOJ 2979 「THUSCH 2017」换桌——多路增广费用流

    题目:https://loj.ac/problem/2979 原来的思路: 优化连边.一看就是同一个桌子相邻座位之间连边.相邻桌子对应座位之间连边. 每个座位向它所属的桌子连边.然后每个人建一个点,向 ...

  4. 洛谷P4003 无限之环(infinityloop)(网络流,费用流)

    洛谷题目传送门 题目 题目描述 曾经有一款流行的游戏,叫做 Infinity Loop,先来简单的介绍一下这个游戏: 游戏在一个 n ∗ m 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在格 ...

  5. 洛谷P4003 无限之环(费用流)

    传送门 神仙题啊……不看题解我可能一年都不一定做得出来……FlashHu大佬太强啦 到底是得有怎样的脑回路才能一眼看去就是费用流啊…… 建好图之后套个板子就好了,那么我们着重来讨论一下怎么建图 首先, ...

  6. 洛谷P4003 [国家集训队2017]无限之环 网络流 最小费用最大流

    题意简述 有一个\(n\times m\)棋盘,棋盘上每个格子上有一个水管.水管共有\(16\)种,用一个\(4\)位二进制数来表示当前水管向上.右.下.左有个接口.你可以旋转除了\((0101)_2 ...

  7. LOJ 2321 清华集训2017 无限之环 拆点+最小费用最大流

    题面:中文题面,这里不占用篇幅 分析: 看到题面,我就想弃疗…… 但是作为任务题单,还是抄了题解…… 大概就是将每个格子拆点,拆成五个点,上下左右的触点和一个负责连源汇点的点(以下简称本点). 这个这 ...

  8. 【题解】洛谷P1073 [NOIP2009TG] 最优贸易(SPFA+分层图)

    次元传送门:洛谷P1073 思路 一开始看题目嗅出了强连通分量的气息 但是嫌长没打 听机房做过的dalao说可以用分层图 从来没用过 就参考题解了解一下 因为每个城市可以走好几次 所以说我们可以在图上 ...

  9. 洛谷P1462 通往奥格瑞玛的道路(二分+spfa,二分+Dijkstra)

    洛谷P1462 通往奥格瑞玛的道路 二分费用. 用血量花费建图,用单源最短路判断 \(1\) 到 \(n\) 的最短路花费是否小于 \(b\) .二分时需要不断记录合法的 \(mid\) 值. 这里建 ...

随机推荐

  1. 帝国cms数据表中文说明

    本文介绍下,帝国cms中各数据表的用途,有需要的朋友,参考下吧. 帝国cms各数据表及用途说明. phome_ecms_infoclass_news 新闻采集规则记录表 phome_ecms_info ...

  2. 【leetcode刷题笔记】Binary Tree Inorder Traversal

    Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...

  3. Maven项目结构

    maven项目主体结构: 另外,Eclipse新建项目时会生成.project..classpath及.settings目录下的文件,这些文件用于描述一个Eclipse项目, 接下来做一个简要的解析: ...

  4. 20145229吴姗珊 《Java程序设计》课程总结

    20145229吴姗珊 <Java程序设计>课程总结 (按顺序)每周读书笔记链接汇总 第一周:http://www.cnblogs.com/20145229ss/p/5248728.htm ...

  5. JavaScript在执行代码之前会校验代码,声明变量提前至当前作用域最前面。

    var name = 123; function getName(){ console.log(name); } getName(); 输出123 -------------------------- ...

  6. expr命令使用

    转载:http://www.cnblogs.com/f-ck-need-u/p/7231832.html expr命令可以实现数值运算.数值或字符串比较.字符串匹配.字符串提取.字符串长度计算等功能. ...

  7. 剑指offer之 替换空格

    package Problem4; public class ReplaceBank { /* * 题目描述: 请实现一个函数,将字符串的每个空格替换为"%20". * 例如输入& ...

  8. 在unigui中为组件添加hint

    //改变hint的样式procedure TUniForm1.UniFormShow(Sender: TObject);var i: Integer;begin for I := 0 to Self. ...

  9. Hive报错 Failed with exception java.io.IOException:java.lang.IllegalArgumentException: java.net.URISyntaxException: Relative path in absolute URI: ${system:user.name%7D

    报错信息如下 Failed with exception java.io.IOException:java.lang.IllegalArgumentException: java.net.URISyn ...

  10. UVA 1664 Conquer a New Region (并查集+贪心)

    并查集的一道比较考想法的题 题意:给你n个点,接着给你n-1条边形成一颗生成树,每条边都有一个权值.求的是以一个点作为特殊点,并求出从此点出发到其他每个点的条件边权的总和最大,条件边权就是:起点到终点 ...