题意:给出一张有m个结点,e条边的带权无向图,其中结点代表码头,边代表航线。试安排n天从1到m的航线,其中有特定时间一些码头不能用(即路线不能经过这个结点),如果当天航线和前一天不同,要额外花费k。求最小花费。
解:试图dp。先设dp[i][j]为第 i 天到结点 j 的最小花费。如果没有时不时关闭的码头,那直接把最短路乘n就可以了。现在有的结点不能用,那选的路线就会和前面不一样。考虑怎么记录路线。边有200条,不可能状压。考虑预处理一些东西。想要记录路线,目的是对比和前一天路线是否一样。如果有两天路线一样,那么这条路上没有两天都禁用的结点。处理每一个连续区间不换路线的最少花费,也就是删除这几天所有被禁节点后的最短路长度乘以天数,这样转移的时候只要枚举哪几天连在一起就可以了。设dp[i]为第i天为止的最小花费,dp[i]=min(dp[j]+cost[j+1][i]),n2复杂度,反正数字小,随便跑。记得开long long。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; #define maxx 105 #define maxn 25 #define maxm 205 #define ll long long #define inf 1000000009 #define mod 998244353 int n,m,k,ed; struct node{ int u,v,w; int nxt; }e[maxm*2]; int head[maxn]={0};int cnt=0; void add(int u,int v,int w){ e[++cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt; } ll dis[maxn],vis[maxn]; ll cost[maxx][maxx]={0}; ll dp[maxx]={0}; int p[maxn][maxx]={0}; ll dijk(){ priority_queue<pair<int,int> > q; dis[1]=0; q.push(make_pair(0,1)); while(!q.empty()){ int now=q.top().second; q.pop(); if(vis[now]) continue; vis[now]=1; for(int i=head[now];i;i=e[i].nxt){ int to=e[i].v; ll f=dis[now]+e[i].w; if(f<dis[to]&&!vis[to]){ dis[to]=f; q.push(make_pair(-dis[to],to)); } } } return dis[m]; } signed main(){ // int T; // scanf("%d",&T); // while(T--){ // // } scanf("%d%d%d%d",&n,&m,&k,&ed); for(int i=0;i<ed;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } int d; scanf("%d",&d); for(int i=0;i<d;i++){ int num,x,y; scanf("%d%d%d",&num,&x,&y); for(int j=x;j<=y;j++) p[num][j]=1; } for(int i=1;i<=n;i++){ for(int j=i;j<=n;j++){ for(int kk=1;kk<=m;kk++) dis[kk]=inf,vis[kk]=0; for(int node=1;node<=m;node++){ for(int day=i;day<=j;day++) { if (p[node][day]){ vis[node]=1; break; } } } cost[i][j]=dijk()*(j-i+1); } } dp[0]=inf; for(int i=1;i<=n;i++){ dp[i]=cost[1][i]; for(int j=0;j<i;j++){ dp[i]=min(dp[i],dp[j]+cost[j+1][i]+k); } } printf("%lld\n",dp[n]); return 0; }