分层图

引入:P4822

我们可以建出一个 \(k + 1\) 层的图,第 \(i\) 层指的是使用了 \(i - 1\) 次的 SpellCard。

每一层的一个点到每一层的另一个按照正常的方式连就行了。

另一种情况就是层与层的连边,也就是使用 SpellCard。

假如说有一条起点为 \(u\) 终点为 \(v\) 长度为 \(w\) 的边。

于是我们按照题意就知道从第 \(i\) 层的 \(u\) 连到第 \(i + 1\) 层的 \(v\) 可以连一条长度为 \(w \div 2\)

同理从第 \(i\) 层的 \(v\) 连到第 \(i + 1\) 层的 \(u\) 可以连一条长度为 \(w \div 2\)。但是这两条边都是有向边。

这样这题的边就连完了,直接从第 \(1\) 层的 \(1\) 号点开始跑最短路,答案就是 \(min(dis_{n \times i})\) 其中 \(1 \le i \le k + 1\)

附上主函数的代码:

int main()
{
	cin >> n >> m >> k;
	for(int i = 1;i <= m;i++)
	{
		int u,v,w;
		cin >> u >> v >> w;
		for(int j = 1;j <= k + 1;j++)
		{
			add_edge(t(u,j),t(v,j),w);
			add_edge(t(v,j),t(u,j),w);
			add_edge(t(u,j),t(v,j + 1),w / 2);
			add_edge(t(v,j),t(u,j + 1),w / 2);
		}
	}
	dij();
	int ans = 1e9;
	for(int i = 1;i <= k + 1;i++)
	{
		ans = min(ans,dis[n * i]);
		// cout << dis[n * i] << '\n';
	}
	cout << ans;
	return 0;
}

然后再给大家两道练习题:P4568,P2939

当然我们也会碰到 \(k\) 比较大的情况,所以我们需要剪掉一些不必要的边,这里不展开,给一个例子:P3831

如果 \(k\) 更大了呢?比如说这道题呢?

很明显,这体不题不能像以前一样建图了(如果有神仙建出来了就另当别论了),那么我们就用新的一种方法来建图 —— DP。

其实想回本质最短路算法也就是一个 DP 罢了。

所以我们对于这道题的定义就显而易见了,定义 \(dp_{x,y,t}\) 为在第 \(x\) 行,第 \(y\) 列还剩 \(t\) 格油。

然后再这张图上跑最短路就行了,这个点和上下左右相连,和 BFS 有点像。

但是在最短路的转移不太一样,需要分类讨论。

  1. 到了 \((x,y)\) 并且不加油,这个情况的判定条件就很简单了,也就是 \(g_{nx,ny}\)\(0\)\(dis_{nx,ny,t.k - 1} \ge dis_{t.x,t.y,t.k} + T\),其中 \(t\) 为队首。

2.到了 \((x,y)\) 加油,看一下就知道了 \(dis_{nx,ny,k} \ge dis_{t.x,t.y,t.k} + T\)

至于 \(T\) 怎么求按照题意模拟即可。

最后贴上代码:

#include<bits/stdc++.h>
using namespace std;
int n,k,a,b,c;
int p[105][105],dis[105][105][45];
bool vis[105][105][45];
struct node
{
	int x,y,k,dis;
	bool operator < (const node &tmp) const
	{
		return dis > tmp.dis;
	}
};
int dx[] = {-1,0,1,0};
int dy[] = {0,-1,0,1};
void dij()
{
	priority_queue<node> q;
	memset(dis,63,sizeof(dis));
	memset(vis,0,sizeof(vis));
	q.push({1,1,k,0});
	dis[1][1][k] = 0;
	while(!q.empty())
	{
		node t = q.top();
		q.pop();
		if(vis[t.x][t.y][t.k])
		{
			continue;
		}
		if(t.k == 0)
		{
			continue;
		}
		vis[t.x][t.y][t.k] = 1;
		// cout << t.x << " " << t.y << " " << t.k << " " << dis[t.x][t.y][t.k] << '\n';
		for(int i = 0;i < 4;i++)
		{
			int xx = t.x + dx[i],yy = t.y + dy[i];
			if(xx < 1 || xx > n || yy < 1 || yy > n)
			{
				continue;
			}
			int T = 0;
			if(dx[i] == -1 || dy[i] == -1)
			{
				T += b;
			}
			int j = t.k;
			if(!p[xx][yy] && dis[xx][yy][j - 1] > dis[t.x][t.y][j] + T)
			{
				dis[xx][yy][j - 1] = dis[t.x][t.y][j] + T;
				q.push({xx,yy,j - 1,dis[xx][yy][j - 1]});
			}
			if(!p[xx][yy])
			{
				T += a + c;
			}
			else
			{
				T += a;
			}
			if(dis[xx][yy][k] > dis[t.x][t.y][j] + T)
			{
				dis[xx][yy][k] = dis[t.x][t.y][j] + T;
				q.push({xx,yy,k,dis[xx][yy][k]});
			}
		}
	}
}
int main()
{
	cin >> n >> k >> a >> b >> c;
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= n;j++)
		{
			cin >> p[i][j];
		}
	}
	dij();
	int ans = 1e9;
	for(int i = 0;i <= k;i++)
	{
		ans = min(ans,dis[n][n][i]);
	}
	cout << ans;
	return 0;
}
posted @ 2023-08-25 10:55  sqrtqwq  阅读(13)  评论(0编辑  收藏  举报