题目
http://www.lydsy.com/JudgeOnline/problem.php?id=1003
题解
spfa+dp
将第i天到第j天的最短路先预处理,记录在cost[i][j]中,f[i]为前i天的最少花费,则可得到
f[i]=min(f[j]+cost[j+1][i]*(i-j)+k), (j<i)
注意从f[0]转移到f[1]时,会被当做修改了路线而加上k,所以f[0]的初值应赋为-k
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1005
#define inf (1<<30)
using namespace std;
int n,m,k,E,d,ans;
int a[N],b[N],w[N],nt[N],p[N];
int cost[N][N];//cost[i][j]记录第i天到第j天最短路的距离
int f[N];//f[i]表示前i天最少花费
bool mark[25][110];//mark[i][j]记录码头i第j天是否能用
int num;
void add(int x,int y,int v)
{
a[++num]=x;b[num]=y;w[num]=v;
nt[num]=p[x];p[x]=num;
}
bool check(int i,int j,int x)//判断码头x在第i天到第j天是否能用
{
for(int t=i;t<=j;t++) if(mark[x][t]) return false;
return true;
}
int q[N*10],dis[N];bool flag[N];
void spfa(int i,int j)
{
for(int i=1;i<=m;i++) dis[i]=inf;
int head=0,tail=0;
q[++tail]=1;flag[1]=1;dis[1]=0;
while(head<tail)
{
int k=q[++head];
for(int e=p[k];e;e=nt[e])
{
int kk=b[e];
if(check(i,j,kk)&&dis[kk]-dis[k]>w[e])
{
dis[kk]=dis[k]+w[e];
if(!flag[kk]) {q[++tail]=kk;flag[kk]=1;}
}
}
flag[k]=0;
}
cost[i][j]=dis[m];//一共有m个码头,第一次写成n了TAT,调N久没调出来
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&E);
for(int i=1;i<=E;i++)
{
int x,y,v;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++)
{
int id,x,y;scanf("%d%d%d",&id,&x,&y);
for(int j=x;j<=y;j++) mark[id][j]=1;
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++) spfa(i,j);
f[0]=-k;
for(int i=1;i<=n;i++) f[i]=inf;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
if(cost[j+1][i]!=inf) f[i]=min(f[i],f[j]+cost[j+1][i]*(i-j)+k);
printf("%d",f[n]);
return 0;
}