一、题目
二、解法
这道题得难点在于如何在限制点的情况下最短路和最小
k
k
k值的决策,考虑动态规划。
每一天的路径可以不改变路径形态,或者改变路径形态。发现在决策过程中,会出现相同且连续的路径形态,我们自然而然地想到了区间类型的
d
p
dp
dp,即对于本题,我们要对一个大区间分段,每断开一次都会导致为
k
k
k的花费,而分段后区间带来的代价减少。
设
f
[
i
]
f[i]
f[i]为对于
[
1
,
i
]
[1,i]
[1,i]执行上述操作的最小花费,则有:
f
[
i
]
=
min
{
f
[
j
−
1
]
+
k
+
c
o
s
t
[
j
]
[
i
]
}
f[i]=\min\{f[j-1]+k+cost[j][i]\}
f[i]=min{f[j−1]+k+cost[j][i]}
现在的问题是,怎么算
c
o
s
t
cost
cost?发现
c
o
s
t
cost
cost即为这个天数区间不能走被限制点的最短路径,考虑到
n
≤
20
n\leq 20
n≤20,我们可以对点状压,取这些天数被限制点集的并集,然后跑
d
i
j
k
s
t
r
a
dijkstra
dijkstra即可。
时间复杂度
O
(
n
2
m
log
m
)
O(n^2m\log m)
O(n2mlogm) 其中 (
n
n
n为点数,
m
m
m为边数)。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,T,k,d,tot,f[25],dis[25],dp[105],sit[105];
struct edge
{
int v,c,next;
} e[405];
struct node
{
int u,c;
bool operator < (node x) const
{
return c>x.c;
}
};
int dijkstra(int a,int b)
{
int S=0;
for(int i=a; i<=b; i++)
S|=sit[i];
memset(dis,0x3f,sizeof dis);
priority_queue<node> q;
q.push(node{1,0});
dis[1]=0;
while(!q.empty())
{
node t=q.top();
q.pop();
for(int i=f[t.u]; i; i=e[i].next)
{
int v=e[i].v,c=e[i].c;
if(dis[v]>dis[t.u]+c && !((1<<v-1)&S))
{
dis[v]=dis[t.u]+c;
q.push(node{v,dis[v]});
}
}
}
return dis[n];
}
signed main()
{
T=read();
n=read();
k=read();
m=read();
for(int i=1; i<=m; i++)
{
int u=read(),v=read(),c=read();
e[++tot]=edge{v,c,f[u]},f[u]=tot;
e[++tot]=edge{u,c,f[v]},f[v]=tot;
}
d=read();
for(int i=1; i<=d; i++)
{
int u=read(),a=read(),b=read();
for(int j=a; j<=b; j++)
sit[j]|=(1<<u-1);
}
for(int i=1; i<=T; i++)
{
dp[i]=inf;
for(int j=1; j<=i; j++)
{
int t;
if((t=dijkstra(j,i))^inf)
dp[i]=min(dp[i],dp[j-1]+k+(i-j+1)*t);
}
}
printf("%d\n",dp[T]-k);
}