今天特别高兴,所以写博客2333
一道浙江省选题
SPFA+DP
可能正常的思维是:
我们可以求出一段最短路让这段路在某一段时间内可以一直使用
然后我们如果求出来每一段时间内的这条路就很容易知道答案
然后我们就可以DP
接下来就是后面的内容了。(我直接把我在洛谷发的题解粘过来了)
相信自己可以看得懂233
//[ZJOI2006]物流运输 luogu.P1772
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
/*题解:
动规g[i][j]表示i到j天一直用同一条最短路的最小费用
即i到j天不改路线的最短路
spfa(i,j)求g[i][j]
至于某个点在这段时间内是不是被更改过的判断
看下面的sum数组,真是奇妙判断法。。
动规f[i]表示从第1天到第i天的最小花费
=min(f[i],f[j]+g[j+1][i]*(i-j)+K)
*/
inline int read(){
char ch=getchar();int num=0;
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
num*=10,num+=ch-'0',ch=getchar();
return num;
}
const int N=105,M=25;
const int E=10005;
/*
The game played by G.S.M. && Goes
It has started
Come on , boy!
*/
struct ss{
int to,nex,va;
}edge[E];
int head[M],ecnt,sum[M][N];
void add(int va,int x,int y){
edge[++ecnt]=(ss){y,head[x],va};
edge[++ecnt]=(ss){x,head[y],va};
head[x]=ecnt-1;head[y]=ecnt;
}void mark(int e,int s,int pos){
for(int i=s;i<=e;i++)
sum[pos][i]=1;
}
int n,m,K,e,d;
int g[N][N],f[N];
inline void gsin(){
n=read(),m=read(),K=read(),e=read();
for(int i=1;i<=e;i++)
add(read(),read(),read());
for(int i=1;i<=n;i++) f[i]=9999999;
//奇妙标记法
d=read();//sum[pos][r]-sum[pos][l]!=0说明不可以
for(int i=1;i<=d;i++)
mark(read(),read(),read());
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
sum[i][j]+=sum[i][j-1];
}
inline void gsout(){
printf("%d",f[n]-K);
}
int dis[M],vis[M];
int spfa(int l,int r)
{
for(int i=1;i<=m;i++) dis[i]=999999,vis[i]=0;
queue<int> q;
q.push(1);dis[1]=0;vis[1]=1;
while(!q.empty()){
int sn=q.front();q.pop();vis[sn]=0;
for(int i=head[sn];i;i=edge[i].nex){
int fn=edge[i].to,val=edge[i].va;
if(sum[fn][r]-sum[fn][l-1]||
dis[fn]<=dis[sn]+val) continue;
dis[fn]=dis[sn]+val;
if(!vis[fn]){
vis[fn]=1;
q.push(fn);
}
}
}
return dis[m];
}
inline void gsdp(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=spfa(i,j);
f[0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
f[i]=min(f[i],f[j]+g[j+1][i]*(i-j)+K);
}
int main()
{
gsin();
gsdp();
gsout();
return 0;
}