P4009 汽车加油行驶问题 题解

这题貌似是个网络流?分层图?广搜?但实际上是个最短路的板子。

第一次做这题感觉上是一个建图加跑 dijstra 的模板,然后就照着打了。

想到对于每一个点,与自己所有相距小于等于 \(k\) 的边进行建立一条花费为 \(w\) 的边,对于每一条 \((x1,y1) 到 (x2,y2)\) 的边的意义是从点 \((x1,y1)\) 满油出发,到点 \((x2,y2)\) 时进行加油的最小花费。

由于强制消费的存在,所以建边时得进行动态维护。

对每一个点利用深搜,向上下左右四个方向搜索与自己距离小于等于 \(k\) 的所有的点,当搜索到点 \((x,y)\) 对应的为 \(1\) 时,进行强制加油,即退出搜索。否则继续搜下去,并且维护费用最小值。

在搜索结束后,出来进行建边即可。

搜索进行最优性剪枝,防止数据全为 \(0\)

复杂度约为 \(O(n^2 \times k^2)\)

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int N=105;
struct node
{
	int to,data;
};
vector<node>a[N*N];
int t[N][N],n,k,ta,tb,tc,f[4][2]={{1,1},{-1,1},{1,-1},{-1,-1}},dis[N*N],vis[N*N],num[N*N];
struct node2
{
	int name,data;
};
priority_queue<node2>q;
bool operator <(node2 fi,node2 se)
{
	return fi.data>se.data; 
}
void dijstra()
{
	q.push({1,0});
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;
	while(!q.empty())
	{
		int x=q.top().name;
		q.pop();
		if(vis[x])continue;
		vis[x]=1;
		int len=a[x].size();
		for(int i=0;i<len;i++)
		{
			if(dis[a[x][i].to]>dis[x]+a[x][i].data)
			{
				dis[a[x][i].to]=dis[x]+a[x][i].data;
				q.push({a[x][i].to,dis[a[x][i].to]});
			}
		}
	}
}
void dfs(int x,int y,int step,int w)
{
	if(x<1||y<1||x>n||y>n||step<0)return;
	if(num[(x-1)*n+y]<=w+(t[x][y]^1)*(tc+ta)+t[x][y]*ta)return;
	num[(x-1)*n+y]=w+(t[x][y]^1)*(tc+ta)+t[x][y]*ta;
	if(t[x][y]==1)return;
	dfs(x-1,y,step-1,w+tb);
	dfs(x,y-1,step-1,w+tb);
	dfs(x+1,y,step-1,w);
	dfs(x,y+1,step-1,w);
}
int main()
{
	scanf("%d%d%d%d%d",&n,&k,&ta,&tb,&tc);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&t[i][j]);
	for(int i=1;i<=n*n;i++)num[i]=1e9;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			dfs(i-1,j,k,tb);
			dfs(i,j-1,k,tb);
			dfs(i+1,j,k,0);
			dfs(i,j+1,k,0);
			for(int l=1;l<=k;l++)
			{
				for(int p=0;p<=l;p++)
				{
					for(int opt=0;opt<4;opt++)
					{
						int xx=i+p*f[opt][0],yy=j+(l-p)*f[opt][1];
						if(xx<1||xx>n||yy<1||yy>n||num[(xx-1)*n+yy]==1e9)continue;
						a[(i-1)*n+j].push_back((node){(xx-1)*n+yy,num[(xx-1)*n+yy]});
						num[(xx-1)*n+yy]=1e9;
					}
				}
			}
			num[(i-1)*n+j]=1e9;
		}
	}
	dijstra();
	printf("%d",dis[n*n]-ta-tc);
	return 0;
}

同时我也自己打了个分层图,很简单,就不说了。

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int N=105;
const int K=15;
struct node
{
	int to,data;
};
int t[N][N],n,k,ta,tb,tc,f[4][2]={{0,1},{1,0},{0,-1},{-1,0}},dis[N][N][K],sq[N][N][K];
struct node2
{
	int x,y,k;
};
queue<node2>q;
void spfa()
{
	q.push((node2){1,1,k});
	memset(dis,0x3f,sizeof(dis));
	dis[1][1][k]=0;
	sq[1][1][k]=1;
	while(!q.empty())
	{
		int x=q.front().x,y=q.front().y,d=q.front().k;
		sq[x][y][d]=0;
		q.pop();
		for(int i=0;i<4;i++)
		{
			int xx=x+f[i][0],yy=y+f[i][1],dt=d-1,w=0;
			if(xx<1||xx>n||yy<1||yy>n)continue;
			if(dt<0)dt=k-1,w=ta+(t[x][y]^1)*tc;
			if(xx<x)w+=tb;
			if(yy<y)w+=tb;
			if(t[xx][yy])dt=k,w+=ta;
			if(dis[x][y][d]+w<dis[xx][yy][dt])
			{
				dis[xx][yy][dt]=dis[x][y][d]+w;
				if(!sq[xx][yy][dt])
				{
					q.push({xx,yy,dt});
					sq[xx][yy][dt]=1;
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d%d%d%d",&n,&k,&ta,&tb,&tc);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&t[i][j]);
	spfa();
	int ans=1e9;
	for(int i=0;i<=k;i++)ans=min(ans,dis[n][n][i]);
	printf("%d",ans);
	return 0;
}
posted @ 2023-02-24 13:26  Gmt丶Fu9ture  阅读(26)  评论(0)    收藏  举报