首页 > 其他分享 >【题解】P3356 火星探险问题

【题解】P3356 火星探险问题

时间:2024-08-12 13:37:52浏览次数:6  
标签:费用 P3356 ch 容量 int 题解 ll 探险

\(\large\mathfrak{1st.\ Preamble|}\) 前言

这都什么年代了网络流 24 题居然还能写题解!

个人认为这篇题解讲的还是比较详细的。

\(\large\mathfrak{2nd.\ Solution|}\) 题解

看到题目的第一眼,我的反应是这样的:这不跟深海机器人问题差不多吗?Ctrl-C Ctrl-V 秒了。

不过我还是讲讲怎么建模吧。

这里我定义点 \((x,y)\) 表示第 \(x\) 行第 \(y\) 列的数,和题目中的是反过来的 (题目里的那种表示方法有点反人类)

建模

把一个点 \((x,y)\) 拆成 \((x,y)_1\) 和 \((x,y)_2\) 两个点,\(x_1\) 为入点,\(x_2\) 为出点。

平坦无障碍(\(0\))

  1. 建边 \((x,y)_1 \rightarrow (x,y)_2\),容量为 \(\inf\),费用为 \(0\),代表这个点可以走无数次,没有收益。
  2. 建边 \((x,y)_2 \rightarrow (x+1,y)_1\),容量为 \(\inf\),费用为 \(0\),代表这个从这个点向南到下面那个点可以走无数次。
  3. 建边 \((x,y)_2 \rightarrow (x,y+1)_1\),容量为 \(\inf\),费用为 \(0\),代表这个从这个点向东到右边那个点可以走无数次。

障碍(\(1\))

没法走,不用管它。

石块(\(2\))

和平坦无障碍的相比,只需要再多加一条边 \((x,y)_1 \rightarrow (x,y)_2\),容量为 \(1\),费用为 \(1\),代表只可以捡一次石块,收益为 \(1\)。

最后跑最大费用最大流即可。

输出

我建完模就直接输出费用,然后开开心心地跑去测样例,丫的,居然要输出路径!我当场懵逼,冥(cha)思(kan)苦(ti)想(jie)后得到了下面的方法:

对于每条路径,从起点开始搜索,每搜到一个点,选一条反向边有剩余容量(说明被走过)的临边走过去,并把反向边的剩余容量减去 \(1\),直到走到终点。

如果看不懂可以看代码:

void put_ans(int a){
    int x=1,y=1;    // 从起点开始搜索
    while(1){
        if(x>=q&&y>=p)return;  // 到达终点
        int u=B(x,y);
        for(int i=head[u];i;i=e[i].pre){
            int v=e[i].to;
            if(!e[i^1].w)continue;  // 反向边有剩余容量
            if(v==A(x+1,y)){x++,e[i^1].w--,printf("%d 0\n",a);break;}  // 向南走
            if(v==A(x,y+1)){y++,e[i^1].w--,printf("%d 1\n",a);break;}  // 向东走
        }
    }
}

\(\large\mathfrak{3rd.\ Code|}\) 代码

我是把边权设为负数跑最小费用最大流的方式求解最大费用最大流。代码压行请见谅。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf=1e18,N=5e3+10,M=5e4+10;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
ll e_cnt=1,head[N];
struct EDGE{ll from,to,w,c,pre;}e[M<<1];
ll n,p,q,s,t,ans,cost;
ll dis[N],pre[N];
inline void add(ll from,ll to,ll w,ll c){
    e[++e_cnt]=(EDGE){from,to,w,c,head[from]};
    head[from]=e_cnt;
}
inline void add_edge(ll u,ll v,ll w,ll c){
    add(u,v,w,c),add(v,u,0,-c);
}
inline int A(int x,int y){return (x-1)*p+y;}    // (x,y)_1
inline int B(int x,int y){return A(x,y)+p*q;}   // (x,y)_2
//------------------最大费用最大流板子---------------------
bool spfa(ll s,ll t){
    bool inq[N];
    memset(pre,-1,sizeof(pre));
    for(int i=1;i<=t;i++)dis[i]=inf,inq[i]=0;
    queue<ll>que;
    dis[s]=0,inq[s]=1,que.push(s);
    while(!que.empty()){
        ll u=que.front();
        que.pop();
        inq[u]=0;
        for(int i=head[u];i;i=e[i].pre){
            if(e[i].w>0){
                ll v=e[i].to,c=e[i].c;
                if(dis[u]+c<dis[v]){
                    dis[v]=dis[u]+c;
                    pre[v]=i;
                    if(!inq[v]){
                        que.push(v);
                        inq[v]=1;
                    }
                }
            }
        }
    }
    return dis[t]!=inf;
}
void mincost(ll s,ll t){
    while(spfa(s,t)){
        ll v=t,flow=inf;
        while(pre[v]!=-1){
            ll i=pre[v],u=e[i].from;
            flow=min(flow,e[i].w);
            v=u;
        }
        v=t;
        while(pre[v]!=-1){
            ll i=pre[v],u=e[i].from;
            e[i].w-=flow;
            e[i^1].w+=flow;
            v=u;
        }
        cost+=dis[t]*flow;
        ans+=flow;
    }
}
//------------end of 最大费用最大流-------------
void put_ans(int a){
    int x=1,y=1;    // 从起点开始搜索
    while(1){
        if(x>=q&&y>=p)return;  // 到达终点
        int u=B(x,y);
        for(int i=head[u];i;i=e[i].pre){
            int v=e[i].to;
            if(!e[i^1].w)continue;  // 反向边有剩余容量
            if(v==A(x+1,y)){x++,e[i^1].w--,printf("%d 0\n",a);break;}  // 向南走
            if(v==A(x,y+1)){y++,e[i^1].w--,printf("%d 1\n",a);break;}  // 向东走
        }
    }
}
int main(){
    n=read(),p=read(),q=read();
    s=p*q*2+1,t=s+1;
    for(int i=1;i<=q;i++){
        for(int j=1;j<=p;j++){
            int x;
            x=read();
            if(x!=1){    // 0和2有三条相同边,合在一起,1啥也不干
                add_edge(A(i,j),B(i,j),inf,0);
                if(i+1<=q)add_edge(B(i,j),A(i+1,j),inf,0);
                if(j+1<=p)add_edge(B(i,j),A(i,j+1),inf,0);
                if(x==2)add_edge(A(i,j),B(i,j),1,-1);  // 2多建一条边
            }
        }
    }
    add_edge(s,A(1,1),n,0);
    add_edge(B(q,p),t,n,0);
    mincost(s,t);
    for(int i=1;i<=ans;i++)put_ans(i);
    return 0;
}

AC 记录

\(\large\mathfrak{4th.\ Postscript|}\) 后记

第一遍编号标错居然可以拿 \(84\) 分(只有 #5 没过),这数据着实有点水……

标签:费用,P3356,ch,容量,int,题解,ll,探险
From: https://www.cnblogs.com/Simon-Wu/p/18354805

相关文章

  • ABC366D 题解
    Solution题意简述给你一个正整数\(N\)和\(N^3\)个非负整数,表示为\(A_{x,y,z}\)其中\(1\leqx,y,z\leqN\)。您将得到以下格式的\(Q\)个查询,必须按顺序处理。对于第\(i\)次查询\((1\leqi\leqQ)\),您将得到一个整数元组\((Lx_i,Rx_i,Ly_i,Ry_i,Lz_i,......
  • 逆序对数列(P2513) - 题解
    [HAOI2009]逆序对数列原题链接题目描述对于一个数列\(\{a_i\}\),如果有\(i<j\)且\(a_i>a_j\),那么我们称\(a_i\)与\(a_j\)为一对逆序对数。若对于任意一个由\(1\simn\)自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为\(k\)的这样自然数数列到底......
  • ABC366F Maximum Composition 题解
    ABC366FMaximumComposition题解题目大意给定\(N\)个一次函数\(f_i(x)=a_ix+b_i\),从中选出\(K\)个函数\(f_{p_1},f_{p_2},\dots,f_{p_K}\),使得\(f_{p_1}(f_{p_2}(\dotsf_{p_K}))\)最大,求最大值。Solve考虑这样一种情况:我已经选定\(p_{k+1},p_{k+1},\dots,p_K\),现......
  • ABC366F 题解
    Solution题意简述现在有\(N\)个线性函数\(f_1,\dots,f_N\)。函数\(f_i(x)=A_ix+B_i\)。找到一个长度为\(K\)的序列\(p=(p_1,\dots,p_k)\),序列元素为\(K\)个大小在\([1,N]\)的不同整数。输出\(f_{p_1}(f_{p_2}(\dotsf_{p_k}(1)\dots))\)可能的最大值。思路贪心......
  • 爱因斯坦求和约定einsum简单例题解读
    概论在爱因斯坦求和约定或einsum()格式字符串中,所有的索引都可以分为两类:自由索引集和求和索引集。它们的区别很简单:自由索引是用于输出规范中的索引。它们与外层for循环相关联。求和索引是所有其他索引:它们出现在参数规范中,但不出现在输出规范中。之所以称为求和索引,是因......
  • ABC366E 题解
    Solution题意简述二维平面上有\(N\)个点\((x_1,y_1),\dots,(x_N,y_N)\)和一个非负整数\(D\)。求有多少对点对\((x,y)\)满足\(\sum\limits_{i=1}^N(|x-x_i|+|y-y_i|)\leD\)。思路发现\(x_i\)与\(y_i\)的捆绑关系不强(其实是几乎没有关联),所以我们分开考虑,也就是先......
  • 记录JSch连接SFTP Exception:Algorithm negotiation fail问题解决
    问题描述:关于正式环境访问外网连接不成功 1、首先检查是否开放防火墙(已确认开放),策略开放后,通过命令连接是否畅通: 通过telnet命令,可以得出,访问畅通。telnet192.168.1.122 2、查看生产环境日志,观察生产环境访问外网服务器异常:抛出异常,提示:算法协商失败com.jcraft.j......
  • [ABC366C] Balls and Bag Query 题解
    [ABC366C]BallsandBagQuery题解题目传送门AT原题传送门首先是题面的翻译:你有一个袋子,给予\(Q\)次操作,操作有三种1\(x\),将一个写有整数\(x\)的球放入袋中。2\(x\),从袋中取出一个写有整数\(x\)的球。3,查询袋中球上的不同整数的数目。整理了一下思......
  • [ABC366D] Cuboid Sum Query 题解
    [ABC366D]CuboidSumQuery题解原题传送门AT原题传送门题意翻译:给予一个\(N\timesN\timesN\)的三维矩阵,有\(Q\)次询问,对于每次询问,给与四个数,分别为\(L_1,R_1,L_2,R_2,L_3,R_3\)求在三维矩阵中\(a[L_1][L_2][L_3]\)到\(a[R_1][R_2][R_3]\)的区间和。三维前缀......
  • 题解:AT_abc366_c [ABC366C] Balls and Bag Query
    题意给你一个可重集,要求支持插入,删除,元素种类查询三种操作。分析直接乱搞,用一个桶记录每种数字的出现次数,再用一个变量\(sum\)记录元素种类数。插入的时候看看当前该元素出现次数是否为\(1\),删除的时候看看当前元素出现次数是否为\(0\),如果是的话让\(sum\)相应加减即可......