首页 > 其他分享 >P3356 火星探测题解

P3356 火星探测题解

时间:2024-02-01 21:22:30浏览次数:18  
标签:费用 P3356 int 题解 cnt cost MAXN dlt 火星

part1 费用流建图

比较显然,把车的数量当成流量,把捡到的石头数量当成费用。然后跑最大费用最大流。

因为费用是针对点而不是边,所以要拆点,每个点分为入点和出点。

  • 对于向下走,向右走建边:从起点的出点终点的入点连边,容量为 \(inf\),费用为 \(0\)。
  • 对于每一个格子,如果当前格子是石头,那么我们想要的效果是,可以经过无数次,但是只有第一次能得到费用,所以我们把经过这个点的情况分为两种:第一次和第二次及之后。所以从入点到出点建两条边,一条边容量为 \(1\),费用为 \(1\),一条边容量为 \(inf\),费用为 \(0\)。
  • 对于每一个格子,如果当前点是障碍,不连接入点和出点,以达到不能经过的效果。
  • 对于每一个格子,如果当前格子是空格,从入点到出点建一条容量为 \(inf\),费用为 \(0\)。表示可以无数次经过,但是没有费用。

part2 找方案

网络流找方案的做法是什么?在残留网络上,如果一条正向边对应的反向边的容量大于 \(0\),那么流经这条边的流量就为其反向边的容量

费用流也是一样的。

所以我们找到那些有流量的边,这些边就是机器人经过的边,建一个新图,在图上跑 DFS 即可。

part3 一些细节

注意到题目中的要求是 “使到达传送器的探测车的数量最多,而且探测车采集到的岩石标本的数量最多”。

在费用流中,如果没有负权边,那么一定是流量越大,最大费用越大。相应的,在本题中,探测车的数量越多,采到的岩石标本一定单调不减,所以直接跑最大费用流是没有问题的。


注意原题中格子的标号方式与普遍的标号方式不同。

Code

// 2024.2.1 20.02
// 20:35 first test 
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5005,MAXM=1e5+5;
struct E{int nxt,to,cap,w;}e[MAXM];
int head[MAXN];
int cnt=1;
void add(int u,int v,int cap,int w){
    e[++cnt]={head[u],v,cap,w};head[u]=cnt;
    e[++cnt]={head[v],u,0,-w};head[v]=cnt;
}
int dis[MAXN],flow[MAXN];
bool in[MAXN];
int las[MAXN];
const int INF=0x3f3f3f3f;
int n,S,T;
bool spfa(){
    queue<int> q;
    for(int i=1;i<=n;i++)   dis[i]=-INF,flow[i]=0;
    flow[S]=INF,dis[S]=0;
    q.push(S),in[S]=1;
    while(!q.empty()){
        int u=q.front();q.pop();in[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(dis[v]<dis[u]+e[i].w&&e[i].cap){
                dis[v]=dis[u]+e[i].w;
                flow[v]=min(flow[u],e[i].cap);
                las[v]=i;
                if(!in[v])  q.push(v),in[v]=1;
            }
        }
    } 
    return dis[T]>-INF;
}
void EK(int &ans,int &cost){
    ans=cost=0;
    while(spfa()){
        int dlt=flow[T];
        ans+=dlt,cost+=dis[T]*dlt;
        int u=T;
        while(u!=S){
            int i=las[u];
            e[i].cap-=dlt,e[i^1].cap+=dlt;
            u=e[i^1].to;
        }
    }
}
int N,M,K;
int id(int x,int y,int z){return (x-1)*M+y+z*N*M;}
int dir[4][2]={{0,1},{1,0}};
int mp[55][55];
#define pii pair<int,int>
#define fi first
#define se second
vector<pii> G[MAXN];
int Print;
void dfs(int u){
    int y=(u%M==0?M:u%M);
    for(pii &t:G[u]){
        int v=t.fi;
        if(!t.se)   continue;
        int ty=(v%M==0?M:v%M);
        if(y==ty)   printf("%d 0\n",Print);
        else printf("%d 1\n",Print);
        dfs(v);
        t.se--;
        break;
    }
}
int main(){
    #ifndef ONLINE_JUDGE
        freopen(".in","r",stdin);
        freopen(".out","w",stdout);
    #endif
    scanf("%d%d%d",&K,&M,&N);
    n=N*M*2+2;S=n-1,T=n;
    for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            int v;scanf("%d",&v);
            if(v==1)    continue;
            mp[i][j]=v;
            
            add(id(i,j,0),id(i,j,1),INF,0);
            if(v==2)    add(id(i,j,0),id(i,j,1),1,1);
            for(int k=0;k<=1;k++){
                int di=i+dir[k][0],dj=j+dir[k][1];
                if(di<1||dj<1||di>N||dj>M)  continue;
                add(id(i,j,1),id(di,dj,0),INF,0);
            }
        }
    }
    add(S,id(1,1,0),K,0);
    add(id(N,M,1),T,K,0);
    
    int ans,cost;
    EK(ans,cost);
    for(int i=2;i<=cnt;i+=2){
        int u=e[i^1].to,v=e[i].to;
        if(v>=1&&v<=N*M&&u>=N*M+1&&u<=N*M*2){//从出点到入点的边
            if(e[i^1].cap)    G[u-M*N].push_back({v,e[i^1].cap});
        }
    }
    for(Print=1;Print<=ans;Print++){
        dfs(1);
    }
    return 0;
}

标签:费用,P3356,int,题解,cnt,cost,MAXN,dlt,火星
From: https://www.cnblogs.com/bwartist/p/18002150

相关文章

  • AT_abc243_g [ABC243G] Sqrt 题解
    可设\(f_i\)为以\(i\)开头的方案数,由于最后由于操作数很多所以不用考虑还剩多少次操作,显然可得状态转移方程\(f_i=\sum\limits_{j=1}^{\sqrti}f_j\),时间复杂度\(O(T+X\sqrtX)\),空间复杂度\(O(X)\),无法接受。考虑如何更优,可以发现在\(T\)次询问中,每次可以直接转移,因此......
  • UVA12032 题解
    题意原题面一堆废话,其实这道题很简单。\(T\)组数据,每组数据给定你一个长度为\(n\)的序列\(a_1...a_n\),在定义\(a_0\)为\(0\)的情况下,假设\(k\)为你的力量系数,在\(\foralli\inn\)时\(a_i-a_{i-1}\lek\),且在按顺序\(1-n\)进行判断时,如果\(a_i-a_{i-1}=k\)......
  • 一般图最小匹配 题解
    首先进行排序,显然只有排序后相邻两个元素匹配才有可能成为答案。我们设\(b_i=a_i-a_{i-1}\),则问题转化为:在\(b\)数组中选\(m\)个数(显然一个\(b\)),两两不能相邻,求这些数和最小值。就和这道题一模一样了,使用反悔贪心。具体的,我们可以将所有\(b_i\)放进小根堆里,每次取出......
  • PMP-6.8 控制资源--问题解决
    一、控制资源基础内容 0.涉及领域:资源管理计划资源管理计划为如何使用、控制和最终释放实物资源提供指南。1.控制资源阶段需参照文档(1)问题日志问题日志用于识别有关缺乏资源、原材料供应延迟,或低等级原材料等问题。(2)经验教训登记册在项目早期获得的经验教训可......
  • CF1753D 题解
    因为最后要找的是“腾出空位”的最小代价。所以不妨把“障碍的移动”转化为“空位的移动”。令\(f_{x,y}\)为:使得\((x,y)\)为空,至少需要多少代价。下面来找转移方程,显然转移方程与空格子移动有关。所以观察空格子移动的规律。若当前格子\((x,y)\)为L。以\((x,y+1)\)......
  • P7031 [NWRRC2016] Anniversary Cake 题解
    作者还在想,居然没什么人写红题题解???咳咳。言归正传。本题没有想象中的那么复杂,咱分类讨论就行了。·若在属于蛋糕的平面直角坐标系中,两支蜡烛的横、纵轴不同,就会有多种切法。如图:           这样,我们随便找一种情况输出就行,反正有SpecialJudge......
  • A+B problem 题解
    先把一个单项式理解为:符号,系数的绝对值,字母,指数。为了方便操作,一口气读完整个字符串(数组),然后去扫描。因为如果第一项为整数的话没有符号,判一判。读入系数的绝对值像快读。如果有\(\texttt{^}\)这个符号,读一下之后的指数。由于只有三个字母,所以可以复制粘贴,不用写冗余的......
  • CEIT算法训练-双指针部分题解(T1-3)
    AnagramSearch题意简述两个字符串\(s\)和\(t\)相似的定义为:\(s\)可以打乱之后重新排列成为\(t\)。题目给出\(a\)和\(b\),问\(a\)中有多少子串(连续的一段)与\(b\)相似。同时,\(a\)中还含有\(?\)字符,他可以等价于任何字符(可以变成任何字符)解题思路实际上,根据题......
  • 题解 P6491 [COCI2010-2011#6] ABECEDA
    传送门。分析两个字符大小关系不变,并且具有传递性,我们可以联想到拓扑排序来解决。因此,我们就通过字符串的大小关系,推断出一些字符的大小关系,然后拓扑排序即可。#include<bits/stdc++.h>#include<vector>#include<string>#include<queue>//#defineintlonglongusing......
  • [ARC154E] Reverse and Inversion 题解
    题目链接点击打开链接题目解法\(\sumj-i\)是不好维护的,考虑把\(j-i\)拆成之和\(i,j\)相关的项可以得到:\(\sum\limits_{i<j}[p_i>p_j](j-i)=\sum\limits_{i=1}^ni(\sum\limits_{j=1}^{i-1}[p_j>p_i]-\sum\limits_{j=i+1}^n[p_j<p_i])=\sum\limits_{i=1}^ni(i-1-\sum\......