[bzoj1066][SCOI2007]蜥蜴(最大流)

题目

传送门
在一个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..
........
........

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值