题目大意
给定一个图,其中有一个是起点,有一个是终点,有货物分n天运输完,已知每一天哪些点开,哪些点闭。每一天要找到一条最短路运输货物。若要改动运输路径,每次改动需要付出一定的代价k。
解题思路
最难解脱的是,可能会想到网络流方面(数据范围比较小)。但这个怎么解决呢?
<留坑>
如果想到用DP做,考虑有什么东西可以利用的。
①每一天要选一条路径,即给一定的代价。
②一段区间内,相同的运输路径路径相同。
③改变运输路径,相当于新的一个时段开始了。k起到了一个时间轴上界点的作用。
④最短路径的长度对于一段连续的时间来说是一定的。因为在这段时间内,点的开闭是没有改动的。
根据此,dp的状态只需要表示时间即可。即设
f[i]
表示1~i天的答案。
Dis[i][j]
表示
[i,j]
时间内的最短运输路径。
根据③,枚举上一个界点j,即新的那段是
(i−j+1)∗Dis[i][j]+k
.
易得dp方程为
f[i]=max(f[i],f[j−1]+(i−j+1)∗Dis[i][j]+k)
最后答案即为 f[n]−k 。
PS:如果答案要一段段累计,那么寻找分割点是很重要的,这个地方一定要缩短思考时间,然后快速定下界点前一段答案和新一段答案究竟是什么。
这个破地方经常耗我2个小时!!!
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 106
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
int to,next,val;
};note edge[N*N];
int tot,head[N];
int i,j,k,l,e,n,m,ans;
int sum[N][N];
int f[N],Dis[N][N];
int dis[N];
bool bz[N];
int qu[N*100];
int u,v,w,q;
void lb(int x,int y,int z){edge[++tot].to=y;edge[tot].next=head[x];edge[tot].val=z;head[x]=tot;}
int spfa(int st,int en){
int i,l,r,x;
memset(dis,127,sizeof(dis));
memset(bz,0,sizeof(bz));
bz[1]=1;dis[1]=0;qu[1]=1;
l=0,r=1;
while(l<r){
x=qu[++l];
for(i=head[x];i;i=edge[i].next){
if(sum[edge[i].to][en]-sum[edge[i].to][st-1])continue;
if(dis[edge[i].to]>dis[x]+edge[i].val){
dis[edge[i].to]=dis[x]+edge[i].val;
if(!bz[edge[i].to]){
bz[edge[i].to]=1;
qu[++r]=edge[i].to;
}
}
}
bz[x]=0;
}
return dis[m];
}
int main(){
scanf("%d%d%d%d",&n,&m,&k,&e);
fo(i,1,e){
scanf("%d%d%d",&u,&v,&w);
lb(u,v,w);lb(v,u,w);
}
scanf("%d",&q);
while(q--){
scanf("%d%d%d",&u,&v,&w);
sum[u][v]++;sum[u][w+1]--;
}
fo(i,1,m)fo(j,1,n)sum[i][j]+=sum[i][j-1];
fo(i,1,m)fo(j,1,n)sum[i][j]+=sum[i][j-1];
fo(i,1,n)fo(j,i,n)Dis[i][j]=spfa(i,j);
memset(f,127,sizeof(f));
f[0]=0;
fo(i,1,n)fo(j,1,i)
if(Dis[j][i]^2139062143)
f[i]=min(f[i],f[j-1]+(i-j+1)*Dis[j][i]+k);
printf("%d",f[n]-k);
return 0;
}