题目
传送门
在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。
每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。
题解
是否从石柱上跳过去对应着最大流算法中的残留网络?
确实是有联系的;
把每个点拆成两个点,表示石柱的顶部和底部
1. 点i有蜥蜴:源点向点 i 连边,容量为1
2. 点i有石柱:石柱顶端的点向石柱低端的点连边,容量为石柱的高度
3. 点i有可以跳到外面:点 i 向汇点连边,容量为inf (目的是保证边的联通性)
4. 点i有可以跳到的点:点 i 和点 j 连双向边,容量为inf
跑最大流即可
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<queue>
using namespace std;
const int N = 805, M = 400010;
const int inf = 1e9;
int n, m, d, cnt, all, s, t;
char str[30];
int head[N], vis[N];
struct edge{
int to, nxt, c;
edge() {}
edge(int x, int y, int z) { to = x, nxt = y, c = z; }
}e[M];
inline int id1(int x, int y) { return (x-1)*m+y; }
inline int id2(int x, int y) { return (x-1)*m+y+n*m; }
inline int dis(int x1, int y1, int x2, int y2) { return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); }
inline void addedge(int x, int y, int z) {
e[cnt] = edge(y, head[x], z); head[x] = cnt ++;
e[cnt] = edge(x, head[y], 0); head[y] = cnt ++;
}
inline bool bfs()
{
queue<int> q; q.push(s); memset(vis, -1, sizeof vis); vis[s] = 1;
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(e[i].c && vis[v] == -1) {
vis[v] = vis[u]+1;
q.push(v);
}
}
}
return vis[t] != -1;
}
inline int dfs(int u, int flow)
{
if(u == t) return flow;
int used = 0, w;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(vis[v] == vis[u]+1 && e[i].c) {
w = dfs(v, min(flow-used, e[i].c));
e[i].c -= w; e[i^1].c += w; used += w;
if(used == flow) return used;
}
}
if(!used) vis[u] = -1;
return used;
}
inline int dinic()
{
int ret = 0;
while(bfs()) ret += dfs(s, inf);
return ret;
}
int main() {
scanf("%d%d%d", &n, &m, &d); memset(head, -1, sizeof head);
s = 0, t = n*m*2+1;//每个点拆成两个点,表示从石柱顶连向石柱下端
for(int i = 1; i <= n; i ++) {
scanf("%s", str+1);
for(int j = 1; j <= m; j ++) if(str[j]-'0' > 0) addedge(id1(i, j), id2(i, j), str[j]-'0');
}//这个点有石柱,石柱顶连向石柱下端,容量为石柱高
for(int i = 1; i <= n; i ++) {
scanf("%s", str+1);
for(int j = 1; j <= m; j ++) if(str[j] == 'L') addedge(s, id1(i, j), 1), all ++;
}//这个点有蜥蜴,源点连向石柱的上端
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
if(i-d < 1 || i+d > n || j-d < 1 || j+d > m) addedge(id2(i, j), t, inf);//可以跳出去,石柱的底连向汇点
for(int x1 = 1; x1 <= n; x1 ++)//距离小于等于d,可以跳到的两个点连边
for(int y1 = 1; y1 <= m; y1 ++)
for(int x2 = 1; x2 <= n; x2 ++)
for(int y2 = 1; y2 <= m; y2 ++) if(dis(x1, y1, x2, y2) <= d*d) addedge(id2(x1, y1), id1(x2, y2), inf);
printf("%d\n", all - dinic());
return 0;
}
总结
自己的建图还没有A
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=1000;
const int inf=1e9;
int n,r,c,d,maxflow,tot;
struct Edge{
int next,to,dis;
}edge[maxn<<1];
int num_edge=-1,head[maxn],cur[maxn],deep[maxn];
queue <int> q;
char ch[101][101],mp[101][101];
int id(int i,int j) {return c*(i-1)+j;}
int calc(int x1,int x2,int y1,int y2){
int x=x1-x2; int y=y1-y2; return x*x+y*y;
}
void add_edge(int from,int to,int dis)
{
edge[++num_edge].next=head[from];
edge[num_edge].dis=dis;
edge[num_edge].to=to;
head[from]=num_edge;
}
void add(int x,int y,int z) {add_edge(x,y,z); add_edge(y,x,0);}
bool bfs(int s,int t)
{
memset(deep,0x7f,sizeof(deep));
while (!q.empty()) q.pop();
for (int i=0; i<=n; i++) cur[i]=head[i];
deep[s]=0; q.push(s);
while (!q.empty())
{
int now=q.front(); q.pop();
for (int i=head[now]; i!=-1; i=edge[i].next)
{
int to=edge[i].to;
if (deep[to]>inf && edge[i].dis){
deep[to]=deep[now]+1;
q.push(to);
if (to==t) return 1;
}
}
}
return deep[t]<inf;
}
int dfs(int now,int t,int limit)
{
if (now==t || !limit) return limit;
int flow=0,f;
for (int i=cur[now]; i!=-1; i=edge[i].next)
{
cur[now]=i; int to=edge[i].to;
if (deep[to]==deep[now]+1 && (f=dfs(to,t,min(limit,edge[i].dis))))
{
flow+=f;
limit-=f;
edge[i].dis-=f;
edge[i^1].dis+=f;
if (!limit) break;
}
}
return flow;
}
void Dinic(int s,int t)
{
while (bfs(s,t))
maxflow+=dfs(s,t,inf);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d",&r,&c,&d);
int S=0,T=r*c*2+1;
for (int i=1; i<=r; i++)
{
scanf("%s",ch[i]+1);
for (int j=1; j<=c; j++)
{
int x=ch[i][j]-'0';
if (x){
add(id(i,j),id(i,j)+r*c,x);
if (i<=d || r-i<=d || j<=d || c-j<=d) add(id(i,j)+r*c,T,inf);
}
}
}
for (int i=1; i<=r; i++)
{
scanf("%s",mp[i]+1);
for (int j=1; j<=c; j++)
if (mp[i][j]=='L') {tot++; add(S,id(i,j),1);}
}
for (int x1=1; x1<=r-1; x1++)
for (int y1=1; y1<=c-1; y1++)
for (int x2=x1+1; x2<=r; x2++)
for (int y2=y1+1; y2<=c; y2++)
if (ch[x1][y1]-'0'>0 && ch[x2][y2]-'0'>0)
if (calc(x1,x2,y1,y2)<=d*d){
add(id(x1,y1)+r*c,id(x2,y2),1); add(id(x2,y2)+r*c,id(x1,y1),1);
}
n=T;//点的数量
for (int i=0; i<=num_edge; i++) printf("%d: %d %d %d\n",i,edge[i^1].to,edge[i].to,edge[i].dis);
Dinic(S,T);
// printf("%d %d\n",tot,maxflow);
printf("%d\n",max(0,tot-maxflow));
return 0;
}
/*
2 2 1
10
01
L.
..
5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........
*/