P4009 汽車加油行駛問題
阿新 • • 發佈:2018-12-13
\(\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\)
\(\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;
}