无限之环
题解
最开始看上去像道模拟,不过看到数据范围就知道不可能了。好吧,正解是网络流,接下来讲一下网络流怎么打这一道题。
我们知道,对于每一个格子的每一个管子,它一定会与附近的一个格子的一个管子相连,这是毋庸置疑的。我们必须要让每个管子都流满否则必定会漏水。
我们发现,整个图最后一定会变成若干个独立的联通块,我们可以将每个格子都连向源点或者汇点,至于怎么连可以通过黑白染色的方法来判断。
对于产生的费用这个最重要的花费,我们可以通过旋转与拆点来表示。一个格子需要拆成五个点,分别代表上下左右中。旋转边的花费我们只需要在内部依次向中心连花费为1的边即可。
不过对于每种不同情况的格子我们都需要分别讨论一下,有点麻烦。
这题主要是情况有点多,都需要特判一下,没其他的了。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define MAXN 400005
#define MAXM 20005
using namespace std;
#define UP(x) x+turn*n*m
#define RI(x) x+((turn+1)&3)*n*m
#define DO(x) x+((turn+2)&3)*n*m
#define LE(x) x+((turn+3)&3)*n*m
#define MD(x) x+(n*m<<2)
typedef long long LL;
#define gc() getchar()
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
int tot,head[MAXM],summ,ans;
int from[MAXN],to[MAXN];
int nxt[MAXN],s1,t1,pre[MAXM];
int dis[MAXM],cap[MAXN],paid[MAXN];
bool vis[MAXM];
int n,m,k,totf;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=gc();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
x*=f;
}
void addEdge(int u,int v,int flow,int cost){
//printf("%d %d %d %d\n",u,v,flow,cost);
from[++tot]=u;to[tot]=v;paid[tot]=cost;
cap[tot]=flow;nxt[tot]=head[u];head[u]=tot;
}
void addedge(int u,int v,int flow,int cost,int tp){
if(tp) u^=v,v^=u,u^=v;
//printf("%d %d %d %d\n",u,v,flow,cost);
addEdge(u,v,flow,cost);addEdge(v,u,0,-cost);
}
int EK(){
int res=0,sum=0;
while(1){
for(int i=0;i<=t1;i++) dis[i]=INF,vis[i]=0,pre[i]=-1;
queue<int> q;while(!q.empty()) q.pop();
dis[s1]=0;vis[s1]=1;q.push(s1);
while(!q.empty()){
int now=q.front();q.pop();vis[now]=0;
//printf("%d\n",now);
for(int i=head[now];~i;i=nxt[i]){
int v=to[i];//printf("%d-->%d %d %d %d\n",now,v,dis[now],cap[i],dis[v]);
if(cap[i]>0&&dis[v]>dis[now]+paid[i]){
//printf("%d-->%d\n",now,v);
dis[v]=dis[now]+paid[i];pre[v]=i;
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
//printf("dis%d %d\n",res,dis[t1]);
if(dis[t1]>INF-1) return totf==sum<<1?res:-1;int minn=INF;
for(int i=pre[t1];~i;i=pre[from[i]])minn=min(minn,cap[i]);
for(int i=pre[t1];~i;i=pre[from[i]])cap[i]-=minn,cap[i^1]+=minn;
res+=dis[t1]*minn;sum+=minn;
//printf("%d %d %d\n",res,minn,dis[t]);
}
}
signed main() {
tot=-1;memset(head,-1,sizeof(head));
read(n);read(m);
s1=0;t1=n*m*5+1;k=0,totf=0;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j){
int turn=0,t=(i+j)&1,shape;
k++;//printf("%d %d %d\n",t,k,MD(k));
if(t) addedge(s1,MD(k),INF,0,0);
else addedge(MD(k),t1,INF,0,0);
if(i) addedge(DO(k-m),UP(k),1,0,t);
if(j) addedge(RI(k-1),LE(k),1,0,t);
read(shape);
if(shape&1)addedge(UP(k),MD(k),1,0,t),++totf;
if(shape&2)addedge(RI(k),MD(k),1,0,t),++totf;
if(shape&4)addedge(DO(k),MD(k),1,0,t),++totf;
if(shape&8)addedge(LE(k),MD(k),1,0,t),++totf;
switch(shape){
case 8:++turn;
case 4:++turn;
case 2:++turn;
case 1:
addedge(RI(k),UP(k),1,1,t);
addedge(DO(k),UP(k),1,2,t);
addedge(LE(k),UP(k),1,1,t);
break;
case 9:++turn;
case 12:++turn;
case 6:++turn;
case 3:
addedge(DO(k),UP(k),1,1,t);
addedge(LE(k),RI(k),1,1,t);
break;
case 13:++turn;
case 14:++turn;
case 7:++turn;
case 11:
addedge(DO(k),LE(k),1,1,t);
addedge(DO(k),UP(k),1,2,t);
addedge(DO(k),RI(k),1,1,t);
break;
}
}
printf("%d\n",EK());
return 0;
}