狼和羊的故事。
狼爱上羊啊爱的疯狂,
谁让他们真爱了一场;
狼爱上羊啊并不荒唐,
他们说有爱就有方向……
有一个 \(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 飞了。好难受啊,红温不写了。
草。

浙公网安备 33010602011771号