发现如果没有限制的话,就是最短路的模板题。但是这道题的关键就是要处理题上的限制。我们就可以用一个数组来存哪一天哪个港口不能走,跑最短路的时候特判一下,dijkstra处理出所有天的最短路径。
由于数据范围n<=100,所以n^2*(elogm)的时间复杂度也随便能跑。
接下来又怎么办呢?就可以考虑dp,看了题解发现dp的思路还是比较简单的。考虑如何设计状态,dp[i]表示在第i天的最小费用,而我们还要枚举j,也就是在第i天之前是否改变了路径。
可以得出转移方程dp[i]=min(dp[i],dp[j]+k+(i-j)*cost[j+1][i])
代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<queue> #include<cstring> using namespace std; const int maxn=1025; const int N=550; priority_queue< pair<long long,int> > q; long long dis[maxn]; struct node{ int nxt,to,val; }edge[maxn*3]; bool vis[maxn]; int head[maxn],cnt; int x,y,v; void add(int x,int y,int v){ edge[++cnt].nxt=head[x]; edge[cnt].to=y; edge[cnt].val=v; head[x]=cnt; } int used[N][N]; int lock[maxn]; int cost[N][N]; long long dp[maxn]; long long min(long long a,long long b){ return a>b?b:a; } int n,m,k,e; int d,p,a,b; long long dijkstra(int x,int y){ memset(vis,false,sizeof(vis)); memset(dis,88,sizeof(dis)); memset(lock,false,sizeof(lock)); for(int i=1;i<=m;i++){ for(int j=x;j<=y;j++) if(used[i][j]) lock[i]=true; } dis[1]=0; q.push(make_pair(0,1)); while(!q.empty()){ int u=q.top().second; q.pop(); if(vis[u]) continue; vis[u]=true; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(dis[v]>dis[u]+edge[i].val&&(!lock[v])){ dis[v]=dis[u]+edge[i].val; q.push(make_pair(-dis[v],v)); } } } return dis[m]; } int main(){ scanf("%d%d%d%d",&n,&m,&k,&e); for(int i=1;i<=e;i++){ scanf("%d%d%d",&x,&y,&v); add(x,y,v);add(y,x,v); } scanf("%d",&d); for(int i=1;i<=d;i++){ scanf("%d%d%d",&p,&a,&b); for(int j=a;j<=b;j++) used[p][j]=1; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) cost[i][j]=dijkstra(i,j); } for(int i=1;i<=n;i++){ dp[i]=(long long)i*cost[1][i]; for(int j=1;j<=i;j++){//在第j天更换 dp[i]=min(dp[i],dp[j]+k+(long long)(i-j)*cost[j+1][i]); } } printf("%lld\n",dp[n]); return 0; }