1. 程式人生 > >P4009 汽車加油行駛問題

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;
}