题解:P13534 [OOI 2023] The way home / 回家的路
题目 The way home / 回家的路
题目链接 CF1801D / 洛谷 P13534
P13534 The way home / 回家的路
题意简述
我们给定
说明/提示
评分说明
本题测试数据分为
| 组别 | 分值 | 必须通过的组 | 备注 | ||||
|---|---|---|---|---|---|---|---|
| 0 | 0 | -- | -- | -- | -- | -- | 样例测试点 |
| 1 | 14 | -- | -- | -- | -- | ||
| 2 | 13 | -- | -- | -- | -- | ||
| 3 | 17 | -- | -- | -- | 0 | ||
| 4 | 19 | -- | -- | 0 | |||
| 5 | 21 | -- | -- | -- | 0, 3, 4 | ||
| 6 | 16 | -- | -- | -- | -- | 0--5 | 离线评测 |
部分分1
首先对于组别
写完之后存在一个小坑点,如果最短路比
期望得分
部分分2
组别
经过特判后期望得分
部分分3
我们思考一下,如果这道题只是简单的最短路,那
我们再想我们来到一个城市的时候,我们其实只需要关注两件事,第一个是前面表演了几次,另一个是到这里剩下的钱。
我们设 dp 状态
此时期望得分来到了
想一想瓶颈再哪里,是在枚举方案到值域的过程,考虑优化。
部分分4
纵使完成题目的过程中可能会不完美,但谁都期望自己能够完美的做出这题,在部分分
发现我们结合部分分
设状态
首先
总时间复杂度为
#include<iostream>
#include<queue>
#include<cstring>
#define ll long long
using namespace std;
struct Qi
{
int to;
int next;
ll w;
} edge[3005];
int head[805], edge_cnt;
ll w[805]; // 每个节点的表演收益
// f[pos][best]={最小表演次数, 剩余金钱},best表示当前最优表演城市
struct X
{
ll num; // 表演次数
ll money;
}f[805][805];
struct L
{
int pos; // 当前节点
int best; // 当前最优表演城市
ll num; // 表演次数
ll money;
friend bool operator < (L a, L b)
{
if (a.num!=b.num)
{
return a.num>b.num;
}
return a.money<b.money;
}
};
priority_queue<L> Q;
void add(int u,int v,ll s)
{
edge_cnt++;
edge[edge_cnt].to=v;
edge[edge_cnt].next=head[u];
edge[edge_cnt].w=s;
head[u]=edge_cnt;
}
int main()
{
int n,m,p,g;
cin>>n>>m>>p>>g;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
f[i][j].num=1e18;
f[i][j].money=0;
}
}
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=m;i++)
{
int u,v,s;
cin>>u>>v>>s;
add(u,v,s);
}
f[1][1].num=0;
f[1][1].money=p;
Q.push({1,1,0,p});
while(!Q.empty())
{
L curr=Q.top();
Q.pop();
int u=curr.pos;
int best=curr.best;
ll curr_show=curr.num;
ll curr_money=curr.money;
// 若当前状态不是最优,跳过
if(curr_show>f[u][best].num) continue;
if(curr_show==f[u][best].num&&curr_money<f[u][best].money) continue;
for (int i=head[u]; i; i=edge[i].next)
{
int v=edge[i].to;
ll s=edge[i].w;
ll now_show=curr_show;
ll now_money=curr_money;
int now_best=best;
// 若当前金钱不足支付路径费用,需在最优城市表演补足
if(now_money<s)
{
ll need=s-now_money;
ll add_show=(need+w[now_best]-1)/w[now_best];
now_show+=add_show;
now_money+=add_show*w[now_best];
}
now_money-=s;
if(w[v]>w[now_best]) now_best=v;
// 若新状态更优,更新并加入队列
if(now_show<f[v][now_best].num)
{
f[v][now_best].num=now_show;
f[v][now_best].money=now_money;
Q.push({v,now_best,now_show,now_money});
}
else if(now_show==f[v][now_best].num&&now_money>f[v][now_best].money)
{
f[v][now_best].money=now_money;
Q.push({v,now_best,now_show,now_money});
}
}
}
ll ans=1e18;
for(int best=1;best<=n;best++)
{
if(f[n][best].num<ans)
{
ans=f[n][best].num;
}
}
cout<<(ans==1e18 ? -1:ans)<<endl;
return 0;
}