ICPC Pacific Northwest Regional Contest 2018 E题
题意:
给你一个n*m的地图,现在在地图中有一个'B',表示一个盗贼,他想要逃离警方的追捕,盗贼可以往上下左右四个方向移动,一个'.'表示盗贼可以走的路。
现在警方可以在地图中除了'.'和'B'的其他地方放置障碍物,障碍物有k种,每种障碍物所需要的费用不一样,现在要你求出防止盗贼逃到地图边界所需要放置障碍物的最少总花费。
做法:
我们可以把'B'看做是一个源点,然后边界上的点看做为汇点,那么也就是我们需要用最少的花费使得源点无法到达汇点,那这就是一个最小割了。
然后我们考虑如何建图:
边界上的点很多,我们考虑增加一个超级汇点,使得每个边界上的点连向超级汇点,再建立一个超级源点,使得他连向'B'这个点(超级源点其实可以省略,加上是为了方便)。
然后我们发现题目给出的权都是点权,然而我们需要将它转换成为边权,于是我们将每个点进行拆点。
假设一个点的坐标为 \((i,j)\) ,我们先将他拆成图中的两个点 \(i*m+j\) 和 \(i*m+j+n*m\) ,然后连边,
如果是'.'或者'B',则边的容量设为无穷大。
如果是可以放置障碍物的点,则边的容量为放置不同障碍物的花费。
注意每个点向四周的连边
然后注意每个边界上的点要和超级汇点连容量为无穷大的边。'B'和超级源点连容量为无穷大的边。
之后求个最大流
代码:
点击展开代码块
#include <bits/stdc++.h>
#define DEBUG
#define d1(x) std::cout << #x " = " << (x) << std::endl
#define d2(x, y) std::cout << #x " = " << (x) << " ," #y " = " << (y) << std::endl
#define disp(arry, fr, to) \
{ \
std::cout << #arry " : "; \
for(int _i = fr; _i <= to; _i++) std::cout << arry[_i] << " "; \
std::cout << std::endl; \
}
#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define vv(T) v(v(T))
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)
#define drep(i,s,t) for(int i=t;i>=s;--i)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 10010;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int n,m;
int c;
char mp[100][100];
ll a[maxn];
int dir[4][2]={-1,0,0,1,1,0,0,-1};
int head[maxn],cnt=0;
struct edge{
int v,next;
ll c;
}e[maxn*2];
void add(int u,int v,ll c){
e[cnt].v=v;e[cnt].c=c;e[cnt].next=head[u];head[u]=cnt++;
e[cnt].v=u;e[cnt].c=0;e[cnt].next=head[v];head[v]=cnt++;
}
ll ans;
int S,T;
int dis[maxn],cur[maxn];
bool bfs(int s,int t){
memset(dis,-1,sizeof(dis));
dis[s] = 0;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
ll c=e[i].c;
if(c && dis[v]==-1){
dis[v]=dis[u]+1;
q.push(v);
if(v == t) return 1;
}
}
}
return dis[t]!=-1;
}
ll dfs(int u,int t,ll flow){
if(u==t) return flow;
ll delta = flow;
for (int i=cur[u];~i;i=e[i].next){
cur[u] = i;
int v=e[i].v;
ll c=e[i].c;
if(c>0 && dis[v]==dis[u]+1){
ll d=dfs(v,t,min(c,delta));
if(!d) dis[v]=0;
e[i].c-=d;
e[i^1].c+=d;
delta-=d;
if(delta==0) break;
}
}
return flow-delta;
}
ll dinic(int s,int t){
ll ans=0;
while(bfs(s,t)){
memcpy(cur,head,sizeof(cur));
// d1(ans);
ans+=dfs(s,t,inf);
}
return ans;
}
int main(){
memset(head,-1,sizeof(head));
cnt=0;
scanf("%d%d%d",&m,&n,&c);
for(int i=0;i<n;i++){
scanf("%s",mp[i]);
}
for(int i=0;i<c;i++){
scanf("%d",&a[i]);
}
S=2*n*m+100,T=S+1;
for (int i=0;i<n;i++){
for (int j=0;j<m;j++){
int u = (i*m+j);
if(mp[i][j] == '.' || mp[i][j] == 'B') add(u,u+n*m,inf);
else add(u,u+n*m,a[mp[i][j]-'a']);
for (int k=0;k<4;k++){
int x=i+dir[k][0];
int y=j+dir[k][1];
if(x<0 || y<0 || x>=n || y>=m) add(u+n*m,T,inf);//边界上的点
else add(u+n*m,x*m+y,inf);
}
if(mp[i][j]=='B') add(S,u,inf);
}
}
ans = dinic(S,T);
// d1(ans);
if(ans>=inf) puts("-1");
else printf("%lld\n",ans);
return 0;
}