【TC SRM 570】CurvyonRails(费用流)

传送门


题解:

先不考虑那个最小化,如何判断有没有解?

将整张图黑白染色,只考虑非障碍格子,源点向每个黑格连容量为2的边,每个白格向汇点连容量为2的边,然后黑格向所有相邻的白格连容量为1的边,很显然有解当且仅当满流。

然后我们对于每个格子拆两个点,表示从横向还是纵向来的,格子之间的连边按照方向来,为了表示出代价,源汇连出来的边全部拆成两个,向两个点分别连容量为1的边,然后两点之间连容量为1,代价为1的边。

跑最小费用最大流。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::string;

class CurvyonRails{
	private:
		static cs int N=2e4+7,M=35;
		int S,T,tot,n,m,id1[M][M],id2[M][M];
		
		struct edge{int to,cap,w,rev;};
		typedef std::vector<edge>::iterator iter;
		std::vector<edge> G[N];iter cur[N];
		inline void adde(int u,int v,int cap,int cost){
			G[u].push_back((edge){v,cap,cost,G[v].size()});
			G[v].push_back((edge){u,0,-cost,G[u].size()-1});
		}
		
		int dis[N],vis[N];
		inline bool SPFA(){
			memset(dis+1,0x3f,sizeof(int)*tot);
			std::queue<int> q;q.push(S);dis[S]=0;
			while(!q.empty()){
				int u=q.front();q.pop();vis[u]=false;cur[u]=G[u].begin();
				for(auto e:G[u])if(e.cap&&dis[e.to]>dis[u]+e.w){
					dis[e.to]=dis[u]+e.w;
					if(!vis[e.to])vis[e.to]=true,q.push(e.to);
				}
			}
			return dis[T]<1e9;
		}
		
		int tot_flow,tot_cost;
		inline int dfs(int u,int flow){
			if(u==T){
				tot_flow+=flow;
				tot_cost+=flow*dis[T];
				return flow;
			}
			int ans=0;vis[u]=true;
			for(iter &e=cur[u];e!=G[u].end();++e)
			if(e->cap&&dis[e->to]==dis[u]+e->w&&!vis[e->to]){
				int delta=dfs(e->to,std::min(flow-ans,e->cap));
				if(delta){
					e->cap-=delta;
					G[e->to][e->rev].cap+=delta;
					if((ans+=delta)==flow)break;
				}
			}
			vis[u]=false;return ans;
		}
		
		inline void Flow(){
			tot_flow=tot_cost=0;
			while(SPFA())dfs(S,0x3f3f3f3f);
		}
	public:
		CurvyonRails(){}
		int getmin(std::vector<string> s){
			n=s.size(),m=s[0].size();
			S=1,T=tot=2;int ct=0,cnt=0;
			for(int re i=0;i<n;++i)
			for(int re j=0;j<m;++j)
			if(s[i][j]!='w')id1[i][j]=++tot,id2[i][j]=++tot;
			for(int re i=0;i<n;++i)
			for(int re j=0;j<m;++j)if(id1[i][j]){
				if((i^j)&1){
					++ct;++cnt;
					adde(id1[i][j],T,1,0);
					adde(id2[i][j],T,1,0);
				}
				else {
					--ct;++cnt;
					adde(S,id1[i][j],1,0);
					adde(S,id2[i][j],1,0);
					for(int re k=0;k<4;++k){
						static cs int dx[]={0,1,0,-1};
						static cs int dy[]={1,0,-1,0};
						int x=i+dx[k],y=j+dy[k];
						if(!~x||x==n||!~y||y==m||s[x][y]=='w')continue;
						if(k&1)adde(id1[i][j],id2[x][y],1,0);
						else adde(id2[i][j],id1[x][y],1,0);
					}
				}
				int cost=s[i][j]=='C'?1:0;
				adde(id1[i][j],id2[i][j],1,cost);
				adde(id2[i][j],id1[i][j],1,cost);
			}
			if(ct)return -1;Flow();
			if(tot_flow!=cnt)return -1;
			return tot_cost;
		}
};

#ifdef zxyoi

CurvyonRails Solver;

signed main(){
	std::cout<<Solver.getmin(
	{"CC..CCCw.CwC..CC.w.C",
 "C.CCCwCCC.w.w..C.w..",
 "wwww...CC.wC.Cw.CC..",
 "CC..CC.w..w.C..CCCC.",
 "CC.CCC..CwwCCC.wCC..",
 "w.C..wwCC.CC.wwwCC..",
 ".CC.CC..CCC..CC.CC.C",
 "Cw....C.C.CCC...CC..",
 "CC.C..Cww.C.CwwwC..w",
 "wCCww..C...CCCCCCC.w",
 "C.CCw.CC.ww...C.CCww",
 "C.C.C.CCwCC..wCCw.Cw",
 "CCC.C...w..C.wC.wCCw",
 "CC.C..C..CCC.CC.C...",
 "C.ww...CCC..CC...CCC",
 "...CCC.CwwwC..www.C.",
 "wwCCCCC.w.C.C...wCwC",
 "CCwC.CwCCC.C.w.Cw...",
 "C.w.wC.CC.CCC.C.w.Cw",
 "CCw.CCC..C..CC.CwCCw",
 "C.wwwww.CwwCCwwwwwww"}

	)<<"\n";
	return 0;
}

#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值