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