题目链接:
https://www.luogu.com.cn/problem/P4009
思路来源博客:
https://blog.csdn.net/qq_45458915/article/details/103630951
思路解释的非常清晰,建议再次查看
算法:1:分层图 网络流 最小费用最大流 Dinic+Spfa 最短路
图解:
1:选择一个n=3的单元来辅助理解思路
1:按剩余油量分为k+1层,图中蓝色的边为,原图中向右或者下,走一步,费用为0,天蓝色为,原图中向上或者向左走一步,费用为b,这两种边是从k层到1层向下一层连的,每一个点都有的,
2:绿色的边是有加油站的点才有的,是从0层到k-1层向,第k层连的边,费用为a,表示被强制加油,并且加满
3:红色的边,是新修建加油站,只有第0层,即油耗尽的那一层,原来没有加油站的点向第k层连边,表示修建并且油加满,费用为a+c
题目大意:给出一个n*n的矩阵表示道路,途中有一些加油站,现在要从点(1,1)到达点(n,n),问最小花费,其中的一些规则如下:
1:汽车只能沿着网格边行驶,装满油后可以行驶k条边,出发时已经装满油
2:汽车经过一条网格边时,若x或y减小,需要花费b元,其余情况没有花费
3:汽车在行驶过程中遇到油库则必须强制加满油,并花费a元
4:在需要时可以在网格点增设临时油库,并支付花费c元(不包括加油的a元)
5:n,k,a,b,c均为正整数,且满足2<=n<=100,2<=k<=10
思路:
1:关于建图,我们也可以直接建分层图,首先抛去油箱与加油站的情况,如果只是要求从起点到终点的最短路,那么我们直接在一层中进行连边,源点连向起点,流量为1,花费为0,然后按照上面的条件2连边,最后让终点连向汇点就好了
2:至于多出来的油箱限制,我们可以将每一种情况视为新的一层,我选择的是将k拆为k+1层图,每一层图代表当前油箱剩余的油还有多少,那么在转移的时候,符合条件的连边就可以直接从(i,j,k)连边到(x,y,k-1)了
3:对于每个油库,因为是强制满油,所以我们可以将该点[0,k-1]层的点都向第k层的点连边,花费为a,只有第k层才能向四周连边
4:而对于非油库的点,最优解肯定是当油箱空了的时候才建立临时油库,所以只需要让第0层的该点向第k层建边,花费为a+c就好了
建图描述:
三维点表示的是(x,y,k),x,y代表坐标,k代表层数
1: 源点->(1,1,k),流量为1,花费为0
2:(x,y,k)->(x,y,k-1),流量为无穷大,花费符合条件2
3:当前点是否为油库:
1:是油库:(x,y,t)t∈[0,k-1]->(x,y,k),流量为无穷大,花费为a
2:不是油库:(x,y,0)->(x,y,k),流量为无穷大,花费为a+c
4:(n,n,t)t∈[0,k]->汇点,流量为无穷大,花费为0
代码:
#include <bits/stdc++.h>
//不知道为什么,这样写,不可以,要写成下边的函数形式
//#define xuhao(i,j,k,n) ((i-1)*n+j+n*n*k)
#define hefa(x,di,top) (di<=x&&x<=top)
using namespace std;
const int maxn=1e5+1e4+2,maxm=1e6+2e4+2e1+5,inf=1<<31-1;
int n,k,s,t,a,b,c,tot=1,oil,head[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn],maxflow,mincost;
bool vis[maxn];
queue<int>q;
const int d[4][2]=
{
{1,0},{-1,0},{0,1},{0,-1}
};
struct edge
{
int to,next,w,dis;
}e[maxm];
int xuhao(int i,int j,int k,int n)
{
return (i-1)*n+j+n*n*k;
}
void addedge(int x,int y,int w,int dis)
{
e[++tot].to=y;e[tot].w=w;e[tot].dis=dis;e[tot].next=head[x];head[x]=tot;
e[++tot].to=x;e[tot].w=0;e[tot].dis=-dis;e[tot].next=head[y];head[y]=tot;
}
bool spfa(int s,int t)
{
memset(dis,0x7f,sizeof(dis));
memset(flow,0x7f,sizeof(flow));
memset(vis,0,sizeof(vis));
q.push(s);vis[s]=1;dis[s]=0;pre[t]=-1;
while(!q.empty())
{
int now=q.front();q.pop();
vis[now]=0;
for(int i=head[now];i;i=e[i].next)
{
int y=e[i].to;
if(e[i].w>0&&dis[y]>dis[now]+e[i].dis)
{
dis[y]=dis[now]+e[i].dis;
pre[y]=now;
last[y]=i;
flow[y]=min(flow[now],e[i].w);
if(!vis[y])
{
q.push(y);
vis[y]=1;
}
}
}
}
return pre[t]!=-1;
}
void dfs()
{
int now=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(now!=s)
{
e[last[now]].w-=flow[t];
e[last[now]^1].w+=flow[t];
now=pre[now];
}
}
int main()
{
ios::sync_with_stdio(0);
scanf("%d %d %d %d %d",&n,&k,&a,&b,&c);
s=0,t=n*n*(k+1)+1;
addedge(s,xuhao(1,1,k,n),1,0);
for(int i=0;i<=k;i++)addedge(xuhao(n,n,i,n),t,inf,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&oil);
if(oil)
{
for(int t=0;t<k;t++)addedge(xuhao(i,j,t,n),xuhao(i,j,k,n),inf,a);
for(int t=0;t<4;t++)
{
int x=i+d[t][0];
int y=j+d[t][1];
if(hefa(x,1,n)&&hefa(y,1,n))
{
int flag=0;
if(x<i||y<j)flag=b;
addedge(xuhao(i,j,k,n),xuhao(x,y,k-1,n),inf,flag);
}
}
}
else
{
for(int t=0;t<4;t++)
{
int x=i+d[t][0];
int y=j+d[t][1];
if(hefa(x,1,n)&&hefa(y,1,n))
{
int flag=0;
if(x<i||y<j)flag=b;
for(int p=1;p<=k;p++)addedge(xuhao(i,j,p,n),xuhao(x,y,p-1,n),inf,flag);
}
}
addedge(xuhao(i,j,0,n),xuhao(i,j,k,n),inf,a+c);
}
}
while(spfa(s,t))dfs();
printf("%d\n",mincost);
return 0;
}