zoukankan      html  css  js  c++  java
  • 洛谷 P4003 无限之环

    题目:https://www.luogu.com.cn/problem/P4003

    简明题意:给你一个矩阵,每个格子都有一种水管,每种水管可以向上下左右这四个方向中的若干个给定的方向延伸,每次可以将一个格子的水管顺或逆时针旋转90°,问使水管全部闭合的最小操作次数

    看起来就非常的可以最小费用最大流

    既然是矩阵,那么肯定要黑白染色,向源点连白点,向汇点连黑点,然后我们考虑拆点,每个格子多拆四个接口方向的点,这样可以表示出来现在水管的状态,对于旋转可以分情况讨论

    • 只有一个方向的点:该方向和和它相对的方向连费用为2的边,说明需要旋转2次;剩下的两个方向连费用为1的边,只需要旋转1次
    • 有两个方向的边:这两个方向分别和和该方向相对的边连费用为1的边,旋转1次的可以表示;旋转2次的可以通过都流这两个边表示
    • 有三个方向的边:将有相对边的边和对边连费用为2的边,说明需要旋转2次;剩下的两个边向没有方向的边连费用为1的边,只需要旋转1次
    • 四个方向的边不需要旋转,只有对着的方向的边不能旋转

    相邻的格子在对应方向上连边

    然后我们记录下所有格子方向的个数和(tot)(tot/2=flow)说明有解,因为两个水管的接口合上成一个了,否则无解

    刚才的连边过程实际上是把旋转中重合的边略去了,画画图就可以很好理解了

    我是分情况讨论的,没有用也不会用什么高超的位运算技巧QAQ

    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    const int N = 2e3;
    const int M = 114514;
    const int inf = 19260817;
    using namespace std;
    struct edges
    {
    	int to,cost,f;
    }edge[M * 2 + 5];
    int nc,tot,num[M + 5],mf,p[M + 5],n,m,a[N + 5],ans,mp[N + 5][N + 5],id[N + 5][N + 5],id_cnt,co[N + 5][N + 5],S,T,nxt[M + 5],head[M + 5],edge_cnt = 1,dis[M + 5],vis[M + 5],q[M + 5],cur[M + 5];
    void add_edge(int u,int v,int w,int f)
    {
    	edge[++edge_cnt] = (edges){v,w,f};
    	nxt[edge_cnt] = head[u];
    	head[u] = edge_cnt;
    }
    void add(int u,int v,int w,int f)
    {
    	if (!nc)
    		swap(u,v);
    	add_edge(u,v,w,f);
    	add_edge(v,u,0,-f);
    }
    void add_chai(int S,int x)
    {
    	add(S,x * 5,inf,0);
    	if (a[x] & 1)
    		add(x * 5,x * 5 + 1,1,0);
    	if (a[x] & 2)
    		add(x * 5,x * 5 + 2,1,0);
    	if (a[x] & 4)
    		add(x * 5,x * 5 + 3,1,0);
    	if (a[x] & 8)
    		add(x * 5,x * 5 + 4,1,0);	
    }
    void rev(int x)
    {
    	if (a[x] == 1)
    		add(x * 5 + 1,x * 5 + 2,1,1),add(x * 5 + 1,x * 5 + 4,1,1),add(x * 5 + 1,x * 5 + 3,1,2);
    	else
    	if (a[x] == 2)
    		add(x * 5 + 2,x * 5 + 1,1,1),add(x * 5 + 2,x * 5 + 3,1,1),add(x * 5 + 2,x * 5 + 4,1,2);
    	else
    	if (a[x] == 3)
    		add(x * 5 + 1,x * 5 + 3,1,1),add(x * 5 + 2,x * 5 + 4,1,1);
    	else
    	if (a[x] == 4)
    		add(x * 5 + 3,x * 5 + 2,1,1),add(x * 5 + 3,x * 5 + 4,1,1),add(x * 5 + 3,x * 5 + 1,1,2);
    	else
    	if (a[x] == 6)
    		add(x * 5 + 3,x * 5 + 1,1,1),add(x * 5 + 2,x * 5 + 4,1,1);
    	else
    	if (a[x] == 7)
    		add(x * 5 + 2,x * 5 + 4,1,2),add(x * 5 + 1,x * 5 + 4,1,1),add(x * 5 + 3,x * 5 + 4,1,1);
    	else
    	if (a[x] == 8)
    		add(x * 5 + 4,x * 5 + 1,1,1),add(x * 5 + 4,x * 5 + 3,1,1),add(x * 5 + 4,x * 5 + 2,1,2);
    	else
    	if (a[x] == 9)
    		add(x * 5 + 1,x * 5 + 3,1,1),add(x * 5 + 4,x * 5 + 2,1,1);
    	else
    	if (a[x] == 11)
    		add(x * 5 + 1,x * 5 + 3,1,2),add(x * 5 + 4,x * 5 + 3,1,1),add(x * 5 + 2,x * 5 + 3,1,1);
    	else
    	if (a[x] == 12)
    		add(x * 5 + 4,x * 5 + 2,1,1),add(x * 5 + 3,x * 5 + 1,1,1);
    	else
    	if (a[x] == 13)
    		add(x * 5 + 4,x * 5 + 2,1,2),add(x * 5 + 1,x * 5 + 2,1,1),add(x * 5 + 3,x * 5 + 2,1,1);
    	else
    	if (a[x] == 14)
    		add(x * 5 + 3,x * 5 + 1,1,2),add(x * 5 + 4,x * 5 + 1,1,1),add(x * 5 + 2,x * 5 + 1,1,1);
    }
    int dfs(int u,int flow)
    {
    	if (u == T)
    		return flow;
    	int sum = 0;
    	p[u] = 1;
    	for (int &i = cur[u];i;i = nxt[i])
    	{
    		int v = edge[i].to,w = edge[i].cost,f = edge[i].f;
    		if (w && dis[u] + f == dis[v] && !p[v])
    		{
    			int res = dfs(v,min(flow,w));
    			edge[i].cost -= res;
    			edge[i ^ 1].cost += res;
    			flow -= res;
    			sum += res;
    			mf += res * f;
    			if (!flow)
    				break;
    		}
    	}
    	return sum;
    }
    int spfa()
    {
    	for (int i = 1;i <= id_cnt * 5 + 4;i++)
    		cur[i] = head[i],vis[i] = 0,dis[i] = inf,p[i] = 0;
    	dis[S] = 0;
    	vis[S] = 1;
    	int l = 1,r = 0;
    	q[++r] = S;
    	while (l <= r)
    	{
    		int u = q[l++];
    		vis[u] = 0;
    		for (int i = head[u];i;i = nxt[i])
    		{
    			int v = edge[i].to,w = edge[i].cost,f = edge[i].f;
    			if (w && dis[u] + f < dis[v])
    			{
    				dis[v] = dis[u] + f;
    				if (!vis[v])
    				{
    					q[++r] = v;
    					vis[v] = 1;
    				}
    			}
    		}
    	}
    	return dis[T];
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i = 1;i <= n;i++)
    		for (int j = 1;j <= m;j++)
    			id[i][j] = ++id_cnt,co[i][j] = (i + j) % 2;
    	for (int i = 1;i <= n;i++)
    		for (int j = 1;j <= m;j++)
    			scanf("%d",&a[id[i][j]]);
    	for (int i = 1;i <= 15;i++)
    		for (int j = 0;j < 4;j++)
    			if (i & (1 << j))
    				num[i]++;
    	S = 1;
    	T = 2;
    	for (int i = 1;i <= n;i++)
    		for (int j = 1;j <= m;j++)
    		{
    			nc = co[i][j];
    			if (co[i][j] == 1)
    				add_chai(S,id[i][j]);
    			else
    				add_chai(T,id[i][j]);
    			tot += num[a[id[i][j]]];
    			rev(id[i][j]);
    			if (id[i - 1][j])
    				add(id[i][j] * 5 + 1,id[i - 1][j] * 5 + 3,1,0);
    			if (id[i][j - 1])
    				add(id[i][j] * 5 + 4,id[i][j - 1] * 5 + 2,1,0);		
    		}
    	int ans = 0;
    	while (spfa() != inf)
    		ans += dfs(S,inf);
    	if (tot / 2 == ans)
    		printf("%d
    ",mf);
    	else
    		printf("-1
    ");
    	return 0;
    }
    
  • 相关阅读:
    c# 调用C++动态库 问题
    Web应用简易框架。
    【转】SVN历史版本删除(为SVN库瘦身)
    程序员7武器序
    小系统单据自动生成存储过程
    【转】数据库和数据仓库的区别
    jQuery之extend 函数
    .NET单元测试断言(Assert)
    c#类型转换操作符:as和is
    oracle 表数据合并
  • 原文地址:https://www.cnblogs.com/sdlang/p/13277806.html
Copyright © 2011-2022 走看看