洛谷 - P4009 汽车加油行驶问题(分层图最短路/最小费用最大流)
題目鏈接:點擊查看
題目大意:給出一個n*n的矩陣表示道路,途中有一些加油站,現在要從點(1,1)到達點(n,n),問最小花費,其中的一些規則如下:
輸出從起點到達終點的最小費用
題目分析:又是條件冗雜的一道題目,不過是一個比較明顯的分層圖問題,又是要求最小花費,我們可以直接涉及一個動態規劃的轉移方程,然后用spfa迭代更新就好了,這個一會直接掛個代碼就行了,因為最短路寫起來比較簡單也比較好理解
然后重點說一下怎么用費用流求解吧,因為這個題目是要求最小花費,所以建好圖后直接跑費用流也是可行的,關于建圖,我們也可以直接建分層圖,首先拋去油箱與加油站的情況,如果只是要求從起點到終點的最短路,那么我們直接在一層中進行連邊,源點連向起點,流量為1,花費為0,然后按照上面的條件2連邊,最后讓終點連向匯點就好了,至于多出來的油箱限制,我們可以將每一種情況視為新的一層,我選擇的是將k拆為k層圖,每一層圖代表當前油箱剩余的油還有多少,那么在轉移的時候,符合條件的連邊就可以直接從(x,y,k)連邊到(xx,yy,k-1)了,對于每個油庫,因為是強制滿油,所以我們可以將該點[0,k-1]層的點都向第k層的點連邊,花費為a,只有第k層才能向四周連邊,而對于非油庫的點,最優解肯定是當油箱空了的時候才建立臨時油庫,所以只需要讓第0層的該點向第k層建邊,花費為a+c就好了,具體細節還真就沒有了,主要還是在代碼實現的過程中一定要細心細心再細心,一開始建邊的時候因為小于等于漏了一個等于號,調了20多分鐘。。太菜了:
三維點表示的是(x,y,k),x,y代表坐標,k代表層數
關于費用流的建圖就到此為止了,按照上述要求建好圖后直接跑最小費用最大流就是答案了
代碼:
最小費用最大流:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=2e5+100;//點const int M=1e6+100;//邊const int bb[4][2]={0,1,0,-1,1,0,-1,0};struct Edge {int to,w,cost,next; }edge[M];int head[N],cnt;void addedge(int u,int v,int w,int cost) {edge[cnt].to=v;edge[cnt].w=w;edge[cnt].cost=cost;edge[cnt].next=head[u];head[u]=cnt++;edge[cnt].to=u;edge[cnt].w=0;edge[cnt].cost=-cost;edge[cnt].next=head[v];head[v]=cnt++; }int d[N],incf[N],pre[N],n;bool vis[N];bool spfa(int s,int t) {memset(d,inf,sizeof(d));memset(vis,false,sizeof(vis));memset(pre,-1,sizeof(pre));queue<int>q;q.push(s);vis[s]=true;incf[s]=inf;d[s]=0;while(!q.empty()){int u=q.front();q.pop();vis[u]=false;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;int cost=edge[i].cost;if(!w)continue;if(d[v]>d[u]+cost){d[v]=d[u]+cost;pre[v]=i;incf[v]=min(incf[u],w);if(!vis[v]){vis[v]=true;q.push(v);}}}}return pre[t]!=-1; }int update(int s,int t) {int x=t;while(x!=s){int i=pre[x];edge[i].w-=incf[t];edge[i^1].w+=incf[t];x=edge[i^1].to;}return d[t]*incf[t]; }void init() {memset(head,-1,sizeof(head));cnt=0; }int solve(int st,int ed) {int ans=0;while(spfa(st,ed))ans+=update(st,ed);return ans; }int get_id(int x,int y,int k)//第k層的(x,y) {return (x-1)*n+y+k*n*n; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);init();int k,a,b,c,st=N-1,ed=st-1;scanf("%d%d%d%d%d",&n,&k,&a,&b,&c);addedge(st,get_id(1,1,k),1,0);//源點->(1,1,k) for(int i=0;i<=k;i++)//(n,n,kk)kk∈[0,k]->匯點 addedge(get_id(n,n,i),ed,inf,0);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){int val;scanf("%d",&val);if(val)//強制加油{for(int t=0;t<k;t++)//(i,j,kk)kk∈[0,k-1]->(i,j,k) addedge(get_id(i,j,t),get_id(i,j,k),inf,a);for(int t=0;t<4;t++)//(i,j,k)->(xx,yy,k-1) {int xx=i+bb[t][0];int yy=j+bb[t][1];if(xx<=0||yy<=0||xx>n||yy>n)continue;int len=0;if(xx<i||yy<j)len=b;addedge(get_id(i,j,k),get_id(xx,yy,k-1),inf,len);}} else{for(int t=0;t<4;t++){int xx=i+bb[t][0];int yy=j+bb[t][1];if(xx<=0||yy<=0||xx>n||yy>n)continue;int len=0;if(xx<i||yy<j)len=b;for(int kk=1;kk<=k;kk++)addedge(get_id(i,j,kk),get_id(xx,yy,kk-1),inf,len);}addedge(get_id(i,j,0),get_id(i,j,k),inf,a+c);}}printf("%d\n",solve(st,ed));return 0; }分層圖最短路(spfa):
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;const int bb[4][2]={0,1,0,-1,1,0,-1,0};int maze[N][N],d[N][N][15],n,K,a,b,c;bool vis[N][N][15];struct Node {int x,y,k;Node(int X,int Y,int K){x=X;y=Y;k=K;} };void spfa() {memset(vis,false,sizeof(vis));memset(d,inf,sizeof(d));queue<Node>q;q.push(Node(1,1,K));vis[1][1][K]=true;d[1][1][K]=0;while(q.size()){Node cur=q.front();q.pop();int x=cur.x;int y=cur.y;int k=cur.k;vis[x][y][k]=false;if(k<K)//加油{if(maze[x][y])//有加油站{if(d[x][y][K]>d[x][y][k]+a){d[x][y][K]=d[x][y][k]+a;if(!vis[x][y][K]){vis[x][y][K]=true;q.push(Node(x,y,K));}}continue;//強制加油}else//沒加油站 {if(d[x][y][K]>d[x][y][k]+a+c){d[x][y][K]=d[x][y][k]+a+c;if(!vis[x][y][K]){vis[x][y][K]=true;q.push(Node(x,y,K));}}} } if(k)//跑路{for(int i=0;i<4;i++){int xx=x+bb[i][0];int yy=y+bb[i][1];if(xx<=0||yy<=0||xx>n||yy>n)continue;int len=0;if(xx<x||yy<y)len=b;if(d[xx][yy][k-1]>d[x][y][k]+len){d[xx][yy][k-1]=d[x][y][k]+len;if(!vis[xx][yy][k-1]){vis[xx][yy][k-1]=true;q.push(Node(xx,yy,k-1));}}}} } }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);scanf("%d%d%d%d%d",&n,&K,&a,&b,&c);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&maze[i][j]);spfa();int ans=inf;for(int i=0;i<=K;i++)ans=min(ans,d[n][n][i]);printf("%d\n",ans);return 0; }?
總結
以上是生活随笔為你收集整理的洛谷 - P4009 汽车加油行驶问题(分层图最短路/最小费用最大流)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洛谷 - P2774 方格取数问题(最小
- 下一篇: 洛谷 - P3358 最长k可重区间集问