分层图
引入: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;
}
当然我们也会碰到 \(k\) 比较大的情况,所以我们需要剪掉一些不必要的边,这里不展开,给一个例子:P3831
如果 \(k\) 更大了呢?比如说这道题呢?
很明显,这体不题不能像以前一样建图了(如果有神仙建出来了就另当别论了),那么我们就用新的一种方法来建图 —— DP。
其实想回本质最短路算法也就是一个 DP 罢了。
所以我们对于这道题的定义就显而易见了,定义 \(dp_{x,y,t}\) 为在第 \(x\) 行,第 \(y\) 列还剩 \(t\) 格油。
然后再这张图上跑最短路就行了,这个点和上下左右相连,和 BFS 有点像。
但是在最短路的转移不太一样,需要分类讨论。
- 到了 \((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;
}