狼和羊的故事。

狼爱上羊啊爱的疯狂,

谁让他们真爱了一场;

狼爱上羊啊并不荒唐,

他们说有爱就有方向……

狼和羊的故事。

有一个 \(n \times m\) 的网格,对于第 \(i\) 行第 \(j\) 列的格子,有一个整数权值 \(a_{i,j} \in \{0,1,2\}\)。你可以沿网格线划分网格,要求划分后的每一部分不能同时出现 \(1\)\(2\)。你需要最小化划分网格的网格线总长度。

\(1 \leq n,m \leq 100\)

网络流建模。为了分割所有的 \(1\)\(2\),考虑将源点 \(s\) 向所有的 \(1\) 连边,将所有的 \(2\) 向汇点连边,同时对于相邻的格子之间连边,容易发现此时的最小割就是原问题答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const long long INF=0x3f3f3f3f3f3f3f3f;
const int N=10000+2,M=50000;
int n,m,s,t,tot_edge;
int from[2*M+10],to[2*M+10],nxt[2*M+10];
long long cap[2*M+10];
int head[N+10],cur[N+10],level[N+10];
bool bfs(){
	for(int i=1;i<=n;i++){
		level[i]=-1;
	}
	queue<int> q;
	level[s]=0;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=head[u];~i;i=nxt[i]){
			int v=to[i];
			if(cap[i]  &&  level[v]==-1){
				level[v]=level[u]+1;
				q.push(v);
			}
		}
	}
	return level[t]!=-1;
}
long long dfs(int u,long long flow){
	if(u==t  ||  flow==0){
		return flow;
	}
	long long ans=0;
	for(int i=cur[u];~i;i=nxt[i]){
		cur[u]=i;
		int v=to[i];
		if(cap[i]  &&  level[v]==level[u]+1){
			long long res=dfs(v,min(flow,cap[i]));
			ans+=res;
			flow-=res;
			cap[i]-=res;
			cap[i^1]+=res;
			if(flow==0){
				break;
			}
		}
	}
	return ans;
}
long long Dinic(){
	long long max_flow=0;
	while(bfs()){
		for(int i=1;i<=n;i++){
			cur[i]=head[i];
		}
		max_flow+=dfs(s,INF);
	}
	return max_flow;
}
void add_edge(int u,int v,long long w){
	from[tot_edge]=u;
	to[tot_edge]=v;
	cap[tot_edge]=w;
	nxt[tot_edge]=head[u];
	head[u]=tot_edge;
	tot_edge++;
}
void init(){
	tot_edge=0;
	memset(nxt,-1,sizeof(nxt));
	memset(head,-1,sizeof(head));
}
int col[110][110],id[110][110];
int main(){
	init();
	int c_n,c_m;
	scanf("%d %d",&c_n,&c_m);
	s=++n;
	t=++n;
	for(int i=1;i<=c_n;i++){
		for(int j=1;j<=c_m;j++){
			scanf("%d",&col[i][j]);
			id[i][j]=++n;
			if(col[i][j]==1){
				add_edge(s,id[i][j],4);
				add_edge(id[i][j],s,0);
			}
			if(col[i][j]==2){
				add_edge(id[i][j],t,4);
				add_edge(t,id[i][j],0);
			}
		}
	}
	for(int i=1;i<=c_n;i++){
		for(int j=1;j<=c_m;j++){
			if(i-1>=1){
				add_edge(id[i][j],id[i-1][j],1);
				add_edge(id[i-1][j],id[i][j],0);
			}
			if(i+1<=c_n){
				add_edge(id[i][j],id[i+1][j],1);
				add_edge(id[i+1][j],id[i][j],0);
			}
			if(j-1>=1){
				add_edge(id[i][j],id[i][j-1],1);
				add_edge(id[i][j-1],id[i][j],0);
			}
			if(j+1<=c_m){
				add_edge(id[i][j],id[i][j+1],1);
				add_edge(id[i][j+1],id[i][j],0);
			}
		}
	}
	printf("%lld",Dinic());
	return 0;
}

狼和羊的故事 - 副本。

有一个 \(n \times n\) 的网格,对于第 \(i\) 行第 \(j\) 列的格子,有一个整数权值 \(a_{i,j}\)。保证对于网格边界上的格子,满足 \(a_{i,j} \in [1,10^9]\),且对于其他格子,满足 \(a_{i,j} \in \{-1,0\}\)。现在你需要对所有满足 \(a_{i,j}=0\) 的格子,将 \(a_{i,j}\) 替换为 \([1,10^9]\) 之间的任意整数。若两个格子相邻且权值均不为 \(-1\),若其权值分别为 \(x\)\(y\),则会产生 \(|x-y|\) 的代价。你需要最小化替换后的总代价。

\(3 \leq n \leq 200\)\(-1 \leq a_{i,j} \leq 10^9\)

考虑弱化条件,将 \(10^9\) 改为 \(2\),原问题如何解决?

在新的问题中,若 \(1\)\(2\) 相邻,则会产生 \(1\) 的贡献。你要求产生 \(1\) 的贡献最少。

考虑将源点向所有的 \(1\) 连边,所有的 \(2\) 向汇点连边,并连接所有相邻的格子,容易发现,此时的最小割就是答案。

狼和羊的故事。

接下来考虑值域更大的情况。容易得出以下结论:

若对于边界上的格子,\(a_{i,j}\) 构成的集合为 \(S\)。则必然存在一种替换方案,使得替换后所有的 \(a_{i,j}\) 构成的集合为 \(S\)

接下来考虑 \(S\) 中所有相邻元素 \(v_1<v_2\),我们计算 \(v_2-v_1\) 产生的贡献。我们将 \(\leq v_1\) 的数视为 \(1\)\(\geq v_2\) 的数视为 \(2\),算出答案后乘以 \(v_2-v_1\)

狼和羊的故事。

接下来你要做的就是不断地求最大流,删除原本连向 \(t\) 的边,变成 \(s\) 连向的边。

然后你写了,发现 T 飞了。好难受啊,红温不写了。

草。

posted @ 2025-12-18 18:53  Oken喵~  阅读(6)  评论(1)    收藏  举报