[SCOI2007] 蜥蜴 题解
发现实际上就是在求有多少只蜥蜴能逃出来。
发现可以将柱子拆成入点和出点两部分,自己的出点向别人的入点连边,自己的入点向自己的出点连边。最后再加一个超级源点 \(S\),连接所有有蜥蜴的柱子入点;再加一个超级汇点 \(T\),连接所有能够跳出地图的柱子。
我们猛然发现:这个问题不就是求最大流吗?
考虑每种边的容量:
-
\(S\) 到入点。由于每个柱子至多有一只蜥蜴,所以容量为 \(1\)。
-
入点到出点。每个柱子只能向外跳 \(h_{i,j}\) 次,所以容量为 \(h_{i,j}\)。
-
出点到入点/ \(T\):这个没有限制,随便跳多少个都行,所以容量无限。
时间复杂度看似是 \(O(n^8)\),但果真如此吗?
考虑 \(d\) 最大为 \(4\),距离 \(\le 4\) 的一共有 \(49\) 个,所以实际边数至多为 \(49n^2\)(而且根本不可能跑满)。
所以时间复杂度为 \(O(49n^6)\)。
突然感觉用 \(FF\) 算法比 \(dinic\) 更优?
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=805,M=35005;
int n,m,de,s,t,id,h[N],d[N];
int k=1,to[M],w[M],nxt[M];
string st[N];int c[N],ans;
struct stone{int h,x,y;}a[N];
void add(int x,int y,int z){
w[++k]=z;to[k]=y;
nxt[k]=h[x];h[x]=k;
w[++k]=0;to[k]=x;
nxt[k]=h[y];h[y]=k;
}int bfs(){
memset(c,-1,sizeof(c));
queue<int>q;c[s]=0;
q.push(s);d[s]=h[s];
while(q.size()){
int x=q.front();q.pop();
for(int i=h[x];i;i=nxt[i]){
int y=to[i];int lv=w[i];
if(lv>0&&c[y]==-1){
c[y]=c[x]+1;d[y]=h[y];
q.push(y);if(y==t) return 1;
}
}
}return 0;
}int dfs(int x,int ans){
if(x==t) return ans;
int sum=ans;
for(int i=d[x];i&∑i=nxt[i]){
d[x]=i;int y=to[i];int lv=w[i];
if(lv<1||c[y]!=c[x]+1) continue;
int zjy=dfs(y,min(sum,lv));
sum-=zjy;w[i]-=zjy;w[i^1]+=zjy;
}return ans-sum;
}int dinic(){
int re=0;
while(bfs())
re+=dfs(s,1e9);
return re;
}int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m>>de;
for(int i=1;i<=n;i++){
string t;cin>>t;t=" "+t;
for(int j=1;j<=m;j++){
if(t[j]=='0') continue;
a[++id]={t[j]-'0',i,j};
}
}for(int i=1;i<=n;i++)
cin>>st[i],st[i]=" "+st[i];
s=id*2+1;t=id*2+2;
for(int i=1;i<=id;i++){
int ru=i*2-1,ch=i*2;
double x=a[i].x,y=a[i].y;
add(ru,ch,a[i].h);
if(st[(int)x][(int)y]!='.')
add(s,ru,1),ans++;
if(x<=de||y<=de||x+de>n||y+de>m)
add(ch,t,1e7);
for(int j=1;j<=id;j++){
if(i==j) continue;
double z=a[j].x,w=a[j].y;
if((x-z)*(x-z)+(y-w)*(y-w)<=de*de)
add(ch,j*2-1,1e7);
}
}cout<<ans-dinic();
return 0;
}