火星探险问题

[网络流24题]火星探险问题

https://ac.nowcoder.com/acm/problem/213905

前置知识

最大费用最大流

建图

每个石头只能被获取一次,就可以考虑网络流的常规做法,把每个点拆成2个点(入点和出点)
(下面所说的连边都是双向边,网络流基础,不知道的同学建议先学习网络流)

考虑入点连向出点的边

如果这个点为障碍,那么入点不向出点连边,即这个点永远不会被经过
如果这个点不为障碍,那么入点向出点连容量为inf,代价为0的边
如果这个点为石头,那么入点向出点连容量为1,代价为1的边(容量为1保证了这个石头只被计算一次)

考虑出点连向入点的边

这种边即原图中点与点的边,从一个点的出点连向其南方和东方的点的入点即可

费用流

我们需要最多的石头,在建边时,我们将石头赋值到了边权,所以我们使用最大费用最大流

输出

跑完最大费用最大流模板后,考虑如何输出
对每个探测车进行dfs
首先要判断探测车走的方向是否正确(只能向南和东走)
其次查看这条边的反向边是否有流量,如果有,则探测车可以走这边,把这条反向边的流量-1

参考代码

#include<bits/stdc++.h>
using namespace std;
int k,n,m,S,T;
int get(int x,int y){
    return (x*m+y-1)*2;
}
const int N=100000,M=1000000,inf=101010;
struct oppo{
    int to,nex,s,w;
}rod[M];
int head[N],tot;
void add(int from,int to,int s,int w)
{
    rod[tot].nex=head[from];
    rod[tot].to=to;
    rod[tot].s=s;
    rod[tot].w=w;
    head[from]=tot++;
}
void link(int from,int to,int s,int w)
{
    add(from,to,s,w);
    add(to,from,0,-w);
}
int d[N],cur[N];
bool f[N];
bool spfa()
{
    queue<int> v;
    memset(f,0,sizeof(f)); 
    memset(d,-1,sizeof(d));
    d[S]=0,cur[S]=head[S];
    v.push(S);
    while(v.size()){
        int lxl=v.front();v.pop();
        f[lxl]=0;
        for(int i=head[lxl];~i;i=rod[i].nex){
            int to=rod[i].to;
            if(d[to]<d[lxl]+rod[i].w&&rod[i].s){
                d[to]=d[lxl]+rod[i].w;
                cur[to]=head[to];
                if(to==T) return 1;
                if(!f[to]){
                    v.push(to);
                    f[to]=1;
                }
            }
        }
    }
    return 0;
}
bool vis[N];
int ANS;
int find(int x,int low)
{
    if(x==T){
        ANS+=d[T]*low;
        return low;
    } 
    vis[x]=1;
    int all=0;
    for(int i=cur[x];~i;i=rod[i].nex)
    {
        cur[x]=i;
        int to=rod[i].to;
        if(!vis[to]&&d[to]==d[x]+rod[i].w&&rod[i].s){
            int t=find(to,min(rod[i].s,low-all));
            if(!t) d[to]=-1;
            rod[i].s-=t;
            rod[i^1].s+=t;
            all+=t;
        }
        if(all==low) break;
    }
    vis[x]=0;
    return all;
}
void dinic()
{
    while(spfa()) while(find(S,inf));
}
void dfs(int x,int k)
{
    if(x==T) return;
    for(int i=head[x];~i;i=rod[i].nex){
        int to=rod[i].to;
        if(to>x&&rod[i^1].s){
            if(x&1&&to!=T){
                if(to==x+1) printf("%d 1\n",k);
                else printf("%d 0\n",k);
            }
            dfs(rod[i].to,k);
            rod[i^1].s--;
            return;
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    cin>>k>>m>>n;
    S=0,T=get(n,m)+2;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            int t;
            scanf("%d",&t);
            if(t!=1) link(get(i,j),get(i,j)^1,inf,0);
            if(t==2) link(get(i,j),get(i,j)^1,1,1);
        }
    getchar();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(j!=m) link(get(i,j)^1,get(i,j+1),inf,0);
            if(i!=n) link(get(i,j)^1,get(i+1,j),inf,0);
        }
    link(S,get(1,1),k,0);
    link(get(n,m)^1,T,inf,0);
    dinic();
    for(int i=1;i<=k;i++)
        dfs(S,i);
    return 0;
} 
全部评论

相关推荐

点赞 评论 收藏
转发
1 收藏 评论
分享
正在热议
# 牛客帮帮团来啦!有问必答 #
1152266次浏览 17151人参与
# 通信和硬件还有转码的必要吗 #
11210次浏览 101人参与
# OPPO开奖 #
19244次浏览 267人参与
# 和牛牛一起刷题打卡 #
19040次浏览 1635人参与
# 实习与准备秋招该如何平衡 #
203445次浏览 3627人参与
# 大厂无回复,继续等待还是奔赴小厂 #
4983次浏览 31人参与
# 不去互联网可以去金融科技 #
20541次浏览 258人参与
# 通信硬件薪资爆料 #
265981次浏览 2484人参与
# 国企是理工四大天坑的最好选择吗 #
2230次浏览 34人参与
# 互联网公司评价 #
97720次浏览 1280人参与
# 简历无回复,你会继续海投还是优化再投? #
25039次浏览 354人参与
# 0offer是寒冬太冷还是我太菜 #
454923次浏览 5124人参与
# 国企和大厂硬件兄弟怎么选? #
53924次浏览 1013人参与
# 参加过提前批的机械人,你们还参加秋招么 #
14647次浏览 349人参与
# 硬件人的简历怎么写 #
82290次浏览 852人参与
# 面试被问第一学历差时该怎么回答 #
19405次浏览 213人参与
# 你见过最离谱的招聘要求是什么? #
28264次浏览 248人参与
# 学历对求职的影响 #
161259次浏览 1804人参与
# 你收到了团子的OC了吗 #
538790次浏览 6388人参与
# 你已经投递多少份简历了 #
344284次浏览 4963人参与
# 实习生应该准时下班吗 #
96990次浏览 722人参与
# 听劝,我这个简历该怎么改? #
63527次浏览 622人参与
牛客网
牛客企业服务