洛谷P4009:汽車加油行駛問題
阿新 • • 發佈:2019-01-30
題目描述
給定一個 N×N 的方形網格,設其左上角為起點◎,座標(1,1) ,X 軸向右為正, Y 軸向下為正,每個方格邊長為 1,如圖所示。
一輛汽車從起點◎出發駛向右下角終點▲,其座標為 (N,N) 。
在若干個網格交叉點處,設定了油庫,可供汽車在行駛途中加油。汽車在行駛過程中應遵守如下規則:
-
汽車只能沿網格邊行駛,裝滿油後能行駛 K 條網格邊。出發時汽車已裝滿油,在起點與終點處不設油庫。
-
汽車經過一條網格邊時,若其 X 座標或 Y 座標減小,則應付費用 B ,否則免付費用。
-
汽車在行駛過程中遇油庫則應加滿油並付加油費用
-
在需要時可在網格點處增設油庫,並付增設油庫費用 C (不含加油費用A )。
- N,K,A,B,C 均為正整數, 且滿足約束: 2≤N≤100,2≤K≤10 。
設計一個演算法,求出汽車從起點出發到達終點所付的最小費用。
解法
一看到這種用網路流解決圖論的題目,我就覺得噁心,構圖會很繁瑣,然後又容易打錯。在比賽中碰上這種題,我肯定會先跳過。。
不說那麼多,我們來講一下解法。
1.首先有兩個東西需要我們處理,一個是當前的油量,一個是當前所用的費用,因為這道題的k(油箱最大額度)很小,只有十。所以我們就想到了以當前的油量分層。我們把第0
我認真了。。】
2.建邊詳情會在程式碼裡面說,(實在是太長了。。)
3.建完邊之後,我們就按照費用流來跑SPFA,然後依次更新路徑上的點,最後返回cost的和就好
(好咯,貼程式碼。。)
#include<cstdio> #include<cstdlib> #include<cstring> #include<queue> using namespace std; int n,k,a,b,c; struct edge{int x,y,next,cos,c;}; edge s[1000010]; int begin,end; int every; int len=1; int first[110010]; int h[110010]; bool visit[110010]; int mmin[110010]; int fa[110010]; void ins(int x,int y,int c,int cos) { len++; s[len].x=x;s[len].y=y;s[len].c=c;s[len].cos=cos; s[len].next=first[x];first[x]=len; len++; s[len].x=y;s[len].y=x;s[len].c=0;s[len].cos=-cos; s[len].next=first[y];first[y]=len; } bool SPFA(int &cost,int &flow) { queue<int> f; f.push(begin); memset(h,63,sizeof(h)); memset(visit,false,sizeof(visit)); mmin[begin]=1000000000; h[begin]=0; visit[begin]=true; while(!f.empty()) { int x=f.front(); f.pop(); visit[x]=false; for(int i=first[x];i!=0;i=s[i].next) { int y=s[i].y; if(h[y]>h[x]+s[i].cos && s[i].c>0) { fa[y]=i; mmin[y]=min(mmin[x],s[i].c); h[y]=h[x]+s[i].cos; if(visit[y]==false) f.push(y); visit[y]=true; } } } if(h[end]==1061109567) return false; flow+=mmin[end]; cost+=mmin[end]*h[end]; int now=end; while(now!=begin) { int i=fa[now]; s[i].c-=mmin[end]; s[i^1].c+=mmin[end]; now=s[i].x; } return true; } int Cost_Flow() { int cost=0,flow=0; while(SPFA(cost,flow)); return cost; } int main() { scanf("%d %d %d %d %d",&n,&k,&a,&b,&c); every=10000; begin=0;end=110001; ins(begin,1,1,0); for(int cent=0;cent<=k;cent++) ins(cent*every+n*n,end,1,0);\\把圖分完層之後,每張圖的(n,n)這個點都向匯點連一條邊。 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int x; scanf("%d",&x); int pos=n*(i-1)+j; if(x==1) for(int cent=k-1;cent>=0;cent--) { if(i>1) ins(cent*every+pos-n,pos,1,a);//這裡的操作,相當於不同層的pos附近的 if(j>1) ins(cent*every+pos-1,pos,1,a);//點向pos連一條a的邊,當然,逆向走要 if(i<n) ins(cent*every+pos+n,pos,1,a+b);//加b。(就是說到pos這個點的時候 if(j<n) ins(cent*every+pos+1,pos,1,a+b);//滿血復活了,必須要加油,花費a。) } else { for(int cent=k;cent>0;cent--) ins(cent*every+pos,pos,1,a+c);//可以選擇在pos建一個 for(int cent=0;cent<=k-1;cent++)//加油站,花費為c,加滿花費為a,總共a+c。 { if(i>1) ins(cent*every+pos-n,(cent+1)*every+pos,1,0);//否則就到下一層相對應 if(j>1) ins(cent*every+pos-1,(cent+1)*every+pos,1,0);//的位置上,費用為0, if(i<n) ins(cent*every+pos+n,(cent+1)*every+pos,1,b);//逆向走要加費用b,到 if(j<n) ins(cent*every+pos+1,(cent+1)*every+pos,1,b);//下一層意味著,油減少1 } } } printf("%d",Cost_Flow()); }//記得陣列開大一點哦!
喜歡小編的關注我哦!Tarjan會特別照料你的。