题目链接:https://pintia.cn/problem-sets/1330210570443206656/problems/1330211663055204353
题意:
思路:每个点要么是平原要么是高原,也就是把若干点分为两个集合,某些点之间还有冲突,典型的最小割模型。假设一个点为u,如果其是平原,将其与源点相连,流量为b(如果将s->u割去,要花费b,也就是将其变为高原的花费);如果其是高原,将其与汇点相连,流量为b(如果将u->t割去,要花费b,也就是将其变为平原的花费)。一个点(u)可能的冲突点(v)有其右方/下方的点,要将u与v相连,流量为a(如果将u->v割去,要花费a,也就是两点一个是平原,一个是高原);同理,要将v与u也相连,流量也为a。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2510;
const int M = 2*N*10;
const int inf = 0x3f3f3f3f;
struct node{
int to,ca,nex;
}g[M];
int n,m,a,b;
int head[N],cnt,cur[N],s,t;
char mp[55][55];
void init(){
cnt=s=0,t=n*m+1;
for(int i=s;i<=t;i++)
head[i]=-1;
}
int getid(int x,int y){
return (x-1)*m+y;
}
void add(int u,int v,int w){
g[cnt]=node{v,w,head[u]};
head[u]=cnt++;
g[cnt]=node{u,0,head[v]};
head[v]=cnt++;
}
int dep[N];
bool bfs(){
for(int i=s;i<=t;i++) dep[i]=0;
dep[s]=1;
queue<int> q;
q.push(s);
while(!q.empty()){
int u = q.front();
q.pop();
if(u==t) return 1;
for(int i=head[u];~i;i=g[i].nex){
int v=g[i].to;
if(!dep[v]&&g[i].ca>0){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return 0;
}
int dfs(int u,int flow){
if(flow==0||u==t)
return flow;
int nowf,ans=0;
for(int& i=cur[u];~i;i=g[i].nex){
int v=g[i].to;
if(dep[v]==dep[u]+1&&g[i].ca>0){
nowf=dfs(v,min(flow,g[i].ca));
if(nowf){
flow-=nowf;
ans+=nowf;
g[i].ca-=nowf;
g[i^1].ca+=nowf;
}
if(!flow) break;
}
}
if(ans==0) dep[u]=0;
return ans;
}
ll dicnic(){
ll ans=0;
int flow;
while(bfs()){
for(int i=s;i<=t;i++) cur[i]=head[i];
while(flow=dfs(s,inf)){
ans+=flow;
}
}
return ans;
}
int main(void){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&n,&m,&a,&b);
init();
for(int i=1;i<=n;i++){
scanf("%s",mp[i]+1);
for(int j=1;j<=m;j++){
int u=getid(i,j),v;
if(mp[i][j]=='.') add(s,u,b);
else add(u,t,b);
if(i!=n){
v=getid(i+1,j);
add(u,v,a);
add(v,u,a);
}
if(j!=m){
v=getid(i,j+1);
add(u,v,a);
add(v,u,a);
}
}
}
printf("%lld\n",dicnic());
}
return 0;
}