P4009 汽车加油行驶问题

\(\color{#0066ff}{题目描述}\)

给定一个 \(N \times N\) 的方形网格,设其左上角为起点o,坐标\((1,1)\)\(X\) 轴向右为正, \(Y\) 轴向下为正,每个方格边长为 \(1\) ,如图所示。

一辆汽车从起点出发驶向右下角终点,其坐标为 \((N,N)\)

在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:

汽车只能沿网格边行驶,装满油后能行驶 \(K\) 条网格边。出发时汽车已装满油,在起点与终点处不设油库。

汽车经过一条网格边时,若其 \(X\) 坐标或 \(Y\) 坐标减小,则应付费用 \(B\) ,否则免付费用。

汽车在行驶过程中遇油库则应加满油并付加油费用 \(A\)

在需要时可在网格点处增设油库,并付增设油库费用 \(C\)(不含加油费用\(A\) )。

\(N,K,A,B,C\) 均为正整数, 且满足约束: \(2\leq N\leq 100,2 \leq K \leq 10\)

设计一个算法,求出汽车从起点出发到达终点所付的最小费用。

\(\color{#0066ff}{输入格式}\)

文件的第一行是 \(N,K,A,B,C\) 的值。

第二行起是一个\(N\times N\)\(0-1\) 方阵,每行 \(N\) 个值,至 \(N+1\) 行结束。

方阵的第 \(i\) 行第 \(j\) 列处的值为 \(1\) 表示在网格交叉点 \((i,j)\) 处设置了一个油库,为 \(0\) 时表示未设油库。各行相邻两个数以空格分隔。

\(\color{#0066ff}{输出格式}\)

程序运行结束时,输出最小费用。

\(\color{#0066ff}{输入样例}\)

9 3 2 3 6
0 0 0 0 1 0 0 0 0
0 0 0 1 0 1 1 0 0
1 0 1 0 0 0 0 1 0
0 0 0 0 0 1 0 0 1
1 0 0 1 0 0 1 0 0
0 1 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0 1
1 0 0 1 0 0 0 1 0
0 1 0 0 0 0 0 0 0

\(\color{#0066ff}{输出样例}\)

12

\(\color{#0066ff}{数据范围与提示}\)

\(2\leq n\leq 100,2\leq k\leq 10\)

\(\color{#0066ff}{题解}\)

可以发现,k只有10,所以可以暴力O(nmk)建立分层图

分层图,每一层油量相同(可以理解为每个点拆成k个点,k个油)

超级源连(1,1),注意,(n,n),的每一层都要连超级汇(考虑所有情况)

相邻两个点,每一层都要连边,还要考虑消耗油和补充油的跨层连边,肯定MLE了qwq

所以考虑全连在第一层上(第一层是满油的点)

把当前点所有油的情况全连在满油上,代表无论什么情况,都可以花费一些钱(要么自建,要么用现成的加油站)把油加满

这样,对于每个点,枚举4个方向,从4个方向对应的每种情况向当前点连边

由题意易得,所有的容量都是1,跑一遍费用流就行了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#define _ 0
#define LL long long
inline LL in()
{
    LL x=0,f=1; char ch;
    while(!isdigit(ch=getchar()))(ch=='-')&&(f=-f);
    while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
    return x*f;
}
int n,k,a,b,c,s,t,max;
const int mod=1005050;
struct node
{
    int to,dis,can;
    node *nxt,*rev;
    node(int to=0,int dis=0,int can=0,node *nxt=NULL):to(to),dis(dis),can(can),nxt(nxt){}
    void *operator new (size_t)
    {
        static node *S=NULL,*T=NULL;
        return (S==T&&(T=(S=new node[1024])+1024)),S++;
    }
};
const int inf=0x7fffffff;
typedef node* nod;
bool vis[mod];
nod head[mod],road[mod];
int dis[mod],change[mod];
int rx[]={1,0,-1,0};
int ry[]={0,1,0,-1};
std::queue<int> q;
inline void add(int from,int to,int dis,int can)
{   
    nod o=new node(to,dis,can,head[from]);
    head[from]=o;
}
inline void link(int from,int to,int dis,int can)
{
    add(from,to,dis,can);
    add(to,from,-dis,0);
    head[from]->rev=head[to];
    head[to]->rev=head[from];
}
inline int id(int x,int y)
{
    return (x-1)*n+y;
}
inline bool spfa()
{
    for(int i=s;i<=t;i++) dis[i]=change[i]=inf,vis[i]=0;
    q.push(s);
    dis[s]=0;
    while(!q.empty())
    {
        int tp=q.front(); q.pop();
        vis[tp]=false;
        for(nod i=head[tp];i;i=i->nxt)
        {
            if(dis[i->to]>dis[tp]+i->dis&&i->can>0)
            {
                dis[i->to]=dis[tp]+i->dis;
                change[i->to]=std::min(change[tp],i->can);
                road[i->to]=i;
                if(!vis[i->to]) vis[i->to]=true,q.push(i->to);
            }
        }
    }
    return change[t]!=inf;
}
inline void mcmf()
{
    int cost=0;
    while(spfa())
    {
        cost+=change[t]*dis[t];
        for(int o=t;o!=s;o=road[o]->rev->to)
        {
            road[o]->can-=change[t];
            road[o]->rev->can+=change[t];
        }
    }
    printf("%d",cost);
}
int main()
{ 
    n=in(),k=in(),a=in(),b=in(),c=in();
    s=0,t=n*n*(k+1)+1,max=n*n;
    link(s,id(1,1),0,1);
    for(int i=0;i<=k;i++) link(id(n,n)+max*i,t,0,1);
    for(int i=0;i<=k;i++) link(n*n+max*i,t,0,1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            int now=id(i,j);
            if(in())
            {
                for(int u=0;u<=k-1;u++)
                    for(int v=0;v<4;v++)
                    {
                        int xx=i+rx[v];
                        int yy=j+ry[v];
                        if(xx>=1&&xx<=n&&yy>=1&&yy<=n)
                        {
                            if(v<=1) link(id(xx,yy)+u*max,now,b+a,1);
                            else link(id(xx,yy)+u*max,now,a,1);
                        }
                    }
            }
            else
            {
                for(int u=1;u<=k;u++) link(now+max*u,now,a+c,1);
                for(int u=0;u<=k-1;u++)
                    for(int v=0;v<4;v++)
                    {
                        int xx=i+rx[v];
                        int yy=j+ry[v];
                        if(xx>=1&&xx<=n&&yy>=1&&yy<=n)
                        {
                            if(v<=1) link(id(xx,yy)+u*max,now+max*(u+1),b,1);
                            else link(id(xx,yy)+u*max,now+max*(u+1),0,1);
                        }
                    }
            }
        }
    mcmf();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/olinr/p/10116229.html
今日推荐