1. 程式人生 > 實用技巧 >loj2321. 「清華集訓 2017」無限之環

loj2321. 「清華集訓 2017」無限之環

題目:https://loj.ac/problem/2321

簡明題意:給你一個矩陣,每個格子都有一種水管,每種水管可以向上下左右這四個方向中的若干個給定的方向延伸,每次可以將一個格子的水管順或逆時針旋轉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\n",mf);
	else
		printf("-1\n");
	return 0;
}