这道题能想到“割点”真的太强了Orz...
主要看的刘汝佳的紫书376页及博客https://blog.csdn.net/wang2147483647/article/details/54988598
做这道题,对“割”的概念又有了更深的理解,突然有点明白S、T集合及割的意义了。
这道题要注意的有两点:
1.边界上的点容量为INF,表示很难被“割走”;2.任意相邻两点间都要加边,容量为b。
之后一直各种过不了,发现是数组大小的问题。。心情复杂.jpg
一般边数开成点数的4倍,而点数开成矩阵大小的平方甚至再多(不MLE的话越大越好。。)
终于过了。。不容易啊55555...附上AC代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAX=2500*4;
int n,m;
struct Edge
{
int to, next, cap, flow;
}edge[MAX*4];
int tot,head[MAX];
int gap[MAX],dep[MAX],pre[MAX],cur[MAX];
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
edge[tot].to=v;edge[tot].cap=w;edge[tot].flow=0;
edge[tot].next=head[u]; head[u]=tot++;
edge[tot].to=u;edge[tot].cap=0;edge[tot].flow=0;
edge[tot].next=head[v];head[v]=tot++;
}
int sap(int st,int en,int n)
{
memset(gap,0,sizeof(gap));
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof(head));
int u=st;
pre[u]=-1;
gap[0]=n;
int maxflow=0;
while(dep[st]<n)
{
if(u==en)
{
int mindis=INF;
for(int i=pre[u];i!=-1;i=pre[edge[i^1].to])
if(mindis>edge[i].cap-edge[i].flow)
mindis=edge[i].cap-edge[i].flow;
for(int i=pre[u];i!=-1;i=pre[edge[i^1].to])
{
edge[i].flow+=mindis;
edge[i^1].flow-=mindis;
}
u=st;
maxflow+=mindis;
continue;
}
bool flag=false;
int v;
for(int i=cur[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u])
{
flag=true;
cur[u]=pre[v]=i;
u=v;
break;
}
}
if(flag)
{
u=v;
continue;
}
else
{
int mindis=n;
for(int i=head[u];i!=-1;i=edge[i].next)
if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<mindis)
{
mindis=dep[edge[i].to];
cur[u]=i;
}
gap[dep[u]]--;
if(gap[dep[u]]==0)
break;
dep[u]=mindis+1;
gap[dep[u]]++;
if(u!=st)
u=edge[pre[u]^1].to;
}
}
return maxflow;
}
inline int id(int i,int j)
{
return (i-1)*m+j;
}
inline int in(int i,int j)
{
return i>=1&&i<=n&&j>=1&&j<=m;
}
int main()
{
int t;
scanf("%d",&t);
int d,f,b;
char ss[55][55];
while(t--)
{
scanf("%d%d",&m,&n);
scanf("%d%d%d",&d,&f,&b);
//memset(ss,0,sizeof(ss));
for(int i=1;i<=n;i++)
scanf("%s",ss[i]+1);
int ans=0;
int s=n*m+1,t=n*m+2;
memset(edge,0,sizeof(edge));
init();
for(int i=1;i<=n;i++)
{
if(ss[i][1]=='.')
{
ss[i][1]='#';
ans+=f;
}
if(ss[i][m]=='.')
{
ss[i][m]='#';
ans+=f;
}
}
for(int j=2;j<m;j++)
{
if(ss[1][j]=='.')
{
ss[1][j]='#';
ans+=f;
}
if(ss[n][j]=='.')
{
ss[n][j]='#';
ans+=f;
}
}
//cout<<"ans="<<ans<<endl;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int iid=id(i,j);
if(ss[i][j]=='#')
{
if(i==1||i==n||j==1||j==m)
add(s,iid,INF);
else
add(s,iid,d);
}
else if(ss[i][j]=='.')
add(iid,t,f);
if(in(i-1,j))
add(iid,id(i-1,j),b);
if(in(i+1,j))
add(iid,id(i+1,j),b);
if(in(i,j-1))
add(iid,id(i,j-1),b);
if(in(i,j+1))
add(iid,id(i,j+1),b);
}
printf("%d\n",ans+sap(s,t,n*m+2));
}
return 0;
}