火星探险问题(最大费用最大流)

Posted hsez-cyx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了火星探险问题(最大费用最大流)相关的知识,希望对你有一定的参考价值。

火星探险问题(luogu)

Solution

容易想到费用流

为解决点权问题,将一个不是障碍的点 i 拆成两个点 ai,bi

从 ai 向 bi 连一条流量为正无穷(表示可以无限次经过),费用为 0(表示这些经过没有收益)的边

若点 i 是石块,则再从 ai 向 bi 连一条流量为1(表示只能经过一次),费用为 1(表示这次经过收益为 1)的边

若点 i 可以到点 j 且 i 和 j 都不是障碍,则从 bi 向 aj 连一条流量为正无穷,费用为 0 的边

从起点向 a1 连一条流量为探测车数,费用为0的边

从 bn 向终点连一条流量为探测车数,费用为0的边

跑最大费用最大流

Code

 

#include <cstdio>
#include <cstdlib>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=3e3,M=1e5;
int pre[N],s,t,car,d[N],n,m,incf[N],a[40][40],inf=1<<30,dis[N];
int head[N],nxt[M],ver[M],edge[M],cost[M],tot=1,maxflow,maxcost;
bool in[N];
int id(int x,int y,int inv)
{
    return (x-1)*m+y+inv*n*m;
}
void add(int u,int v,int w,int c)
{
    ver[++tot]=v,nxt[tot]=head[u],edge[tot]=w,cost[tot]=c,head[u]=tot;
    ver[++tot]=u,nxt[tot]=head[v],edge[tot]=0,cost[tot]=-c,head[v]=tot;
}
bool spfa()
{
    memset(dis,0x80,sizeof(dis));
    memset(pre,0,sizeof(pre));
    int last=dis[s];
    queue <int> q;
    dis[s]=0,q.push(s),incf[s]=1<<30;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        in[u]=false;
        for(int i=head[u],v;i;i=nxt[i])
            if(edge[i]>0 && dis[v=ver[i]]<dis[u]+cost[i])
            {
                dis[v]=dis[u]+cost[i];
                incf[v]=min(edge[i],incf[u]);
                pre[v]=i;
                if(!in[v]) in[v]=true,q.push(v);
            }
    }
    return dis[t]!=last;
}
void update()
{
    maxflow+=incf[t],maxcost+=incf[t]*dis[t];
    int x=t;
    while(x!=s)
    {
        int i=pre[x];
        edge[i]-=incf[t],edge[i^1]+=incf[t];
        x=ver[i^1];
    }
}
void dfs(int x,int y,int pos,int k)
{
    int kx,ky,mov;
    for(int i=head[pos];i;i=nxt[i])
    {
        int v=ver[i];
        if(v==s || v==t || v==pos-n*m || edge[i^1]<=0) continue;
        edge[i^1]--;
        if(v>n*m)
        {
            dfs(x,y,v,k);
            return;
        }
        if(v==(x-1)*m+y+1) kx=x,ky=y+1,mov=1;
        else kx=x+1,ky=y,mov=0;
        printf("%d %d
",k,mov);
        dfs(kx,ky,v+n*m,k);
        return;
    }
}
int main()
{
    scanf("%d%d%d",&car,&m,&n);
    s=0,t=n*m*2+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    if(a[n][m]==1 || a[1][1]==1) return 0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(a[i][j]==1) continue;
            add(id(i,j,0),id(i,j,1),inf,0);
            if(a[i][j]==2) add(id(i,j,0),id(i,j,1),1,1);
            if(i<n && a[i+1][j]!=1) add(id(i,j,1),id(i+1,j,0),inf,0);
            if(j<m && a[i][j+1]!=1) add(id(i,j,1),id(i,j+1,0),inf,0);
        }
    add(s,id(1,1,0),car,0),add(id(n,m,1),t,car,0);
    while(spfa()) update();
    for(int i=1;i<=maxflow;i++)
        dfs(1,1,1,i);
    return 0;
}

 

 

 

以上是关于火星探险问题(最大费用最大流)的主要内容,如果未能解决你的问题,请参考以下文章

火星探险问题 网络流

最小费用最大流算法

最小费用最大流问题

POJ 2195 & HDU 1533 Going Home(最小费用最大流)

模板最小费用最大流

费用流伪代码