【题意】:一共要运输n天物资,每天都要从1号码头运送到m号码头,给你e行,每行告诉你从a号码头到b号码头的花费(无向图)。再告诉你d行,表示p号码头,在第 [a,b] 天内不能经过。当你某一天和上一天的运输路线不同时,需要额外花费k。现在让你求这n天加起来最小的总花费。
n<=20, m<=100
【题解】:代表前 i 天的最小花费,那么 一定是从某一个 转移过来,并且这些天里走的是同一条路。用 表示天内走同一条路的最小花费。
那么可以得出dp方程:
c[ ][ ]只要先预先跑一遍最短路即可。
【代码】:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,e,a,b,C;
ll c[25][25];
bool fre[25][105],vis[105];
ll cost[105][105];
ll inf=(1ll<<63)-1;
ll dp[105];
void dijkstra(int l,int r)
{
for(int i=0;i<=100;i++) vis[i]=0;
set<pair<ll,int> >st;
st.clear();
int flag=0;
st.insert(make_pair(0,1));
while(!st.empty()){
int x=st.begin()->second;
ll f=st.begin()->first;
st.erase(st.begin());
if(vis[x]==1) continue;
vis[x]=1;
if(x==m){
flag=1;
cost[l][r]=f;
break;
}
for(int i=0;i<=m;i++){
if(vis[i]==0&&c[x][i]){
int flag2=0;
for(int j=l;j<=r;j++){
if(fre[i][j]==1){
flag2=1;
break;
}
}
if(flag2==0){
st.insert(make_pair(f+c[x][i],i));
}
}
}
}
if(flag==0){
cost[l][r]=inf;
}
}
int main(void)
{
scanf("%d%d%d%d",&n,&m,&k,&e);
for(int i=0;i<e;i++){
scanf("%d%d%d",&a,&b,&C);
if(c[a][b]==0) c[a][b]=c[b][a]=C;
else c[a][b]=c[b][a]=min((ll)C,c[a][b]);
}
int d;
scanf("%d",&d);
for(int i=0;i<d;i++){
scanf("%d%d%d",&C,&a,&b);
for(int j=a;j<=b;j++){
fre[C][j]=1;
}
}
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
dijkstra(i,j);
}
}
dp[0]=-k;//第一天不用换路线
for(int i=1;i<=n;i++){
dp[i]=inf;
for(int j=1;j<=i;j++){
if(cost[j][i]<inf)
dp[i]=min(dp[i],dp[j-1]+cost[j][i]*(i-j+1)+k);
}
}
printf("%lld\n",dp[n]);
return 0;
}