题目大意
给出m个点e条带权边,在n天内每天从1到n走一遍,边权即为代价,每次更换线路需要k点代价,问最小的代价。
题解
考虑到数据范围很小,可以先预处理出所有时间区间内的从1到n的最短路的长度dis[i][j],然后进行dp。
初始状态: f[i][j]=dis[i][j]
dp方程为: f[i][j]=min(f[i][k-1]+f[k][j]+K) (i
代码:
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
const int maxn=25, maxl=105;
int tim,n,K,m;
bool occ[maxl][maxl][maxn];
int tot=0,head[maxn];
struct Edge {
int from,to,val,next;
Edge() {}
Edge(int x,int y,int v,int nx):from(x),to(y),val(v),next(nx) {}
}eage[maxn*maxn];
void add(int x,int y,int val) {
eage[tot]=Edge(x,y,val,head[x]), head[x]=tot++;
eage[tot]=Edge(y,x,val,head[y]), head[y]=tot++;
return;
}
const long long INF=(1ll<<52);
long long dis[maxl][maxl][maxn];
bool used[maxn];
queue<int> que;
void spfa(long long *dis,bool *occ) {
for(int i=1;i<=n;i++) dis[i]=INF, used[i]=false;
while(que.size()) que.pop();
dis[1]=0;
used[1]=true;
que.push(1);
while(que.size()) {
int u=que.front(); que.pop();
used[u]=false;
for(int i=head[u];~i;i=eage[i].next) if(dis[eage[i].to]>dis[u]+eage[i].val) {
int v=eage[i].to;
if(occ[v]) continue;
dis[v]=dis[u]+eage[i].val;
if(!used[v]) {used[v]=true; que.push(v);}
}
}
return;
}
long long f[maxn][maxn];
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE
scanf("%d%d%d%d",&tim,&n,&K,&m);
for(int i=1;i<=n;i++) head[i]=-1;
for(int i=1;i<=m;i++) {
int x,y,val;
scanf("%d%d%d",&x,&y,&val);
add(x,y,val);
}
int p;
scanf("%d",&p);
for(int t=0;t<p;t++) {
int id,l,r;
scanf("%d%d%d",&id,&l,&r);
for(int i=1;i<=tim;i++)
for(int j=i;j<=tim;j++) if((l>=i && l<=j) || (r>=i && r<=j))
occ[i][j][id]=true;
}
for(int i=1;i<=tim;i++)
for(int j=i;j<=tim;j++)
spfa(dis[i][j],occ[i][j]);
for(int i=1;i<=tim;i++)
f[i][i]=dis[i][i][n];
for(int len=2;len<=tim;len++)
for(int i=1;i+len-1<=tim;i++) {
int j=i+len-1;
f[i][j]=dis[i][j][n]*len;
for(int k=i+1;k<=j;k++) f[i][j]=min(f[i][j],f[i][k-1]+f[k][j]+K);
}
printf("%lld\n",f[1][tim]);
return 0;
}