DP + 图论好题
思路:首先DP思路显而易见,题目中有的日期需要修改路线,根据线性DP最经典的套路,就是在一个序列上
即f[i] = max(f[i],f[k] + value(k,i))
然后就是需要算价值的部分,我们肯定要处理最短路,用最短路的值来更新答案,题目中支持离线算法,也就是哪些天不能使用的码头是明确知道的,我们DP的是让k to i 这些天都用同一个运输的路线,这时,我们就需要考虑如何让这些天共用一个最短的运输路径,一开始我的思路是判断每天将不能用的点删去之后比较,但是自己怎么也想不出如何保证这些天可以用同一条路径。关键就在于一个思路,这条最短路需要让k to i这所有的天都能用,意味着即使有一天无法用也不行,就是说必须满足所有天的所有条件,不妨就在一张图上将这些天所有无法用的天一起删去,然后跑一边SPFA或者Dij,数据非常小,所以预处理什么的空空间时间完全足够。
代码:
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int M=30; const int N=M*M; const int INF=0X3f3f3f3f; int m,n,k,e,d,t; int head[M],dis[M]; bool close[N][M],book[M],vis[M]; long long cost[N][N],f[N]; struct node{ int to,w,next; } edge[N]; void add(int u,int v,int w){ edge[ ++ d].to = v; edge[d].w = w; edge[d].next = head[u]; head[u] = d; } int spfa(int x,int y){ memset(dis,INF,sizeof(dis)); memset(vis,0,sizeof(vis)); memset(book,0,sizeof(book)); for(int i = 1;i <= n;i ++){ for(int j = x;j <= y;j ++) if(close[i][j]) book[i] = 1; } queue<int> q; q.push(1); dis[1] = 0; vis[1] = 1; while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u];i;i = edge[i].next){ int v = edge[i].to; if(book[v]) continue; if(dis[v] > dis[u] + edge[i].w){ dis[v] = dis[u] + edge[i].w; if(!vis[v]){ q.push(v); vis[v] = 1; } } } } return dis[m]; } int main() { scanf("%d %d %d %d",&n,&m,&k,&e); for(int i = 1;i <= e;i ++){ int u,v,w; scanf("%d %d %d",&u,&v,&w); add(u,v,w); add(v,u,w); } scanf("%d",&t); for(int i = 1;i <= t;i ++){ int p,u,v; scanf("%d %d %d",&p,&u,&v); for(int j = u;j <= v;j ++){ close[p][j]=1; } } for(int i = 1;i <= n;i ++){ for(int j = 1;j <= n;j ++){ cost[i][j] = spfa(i, j); } } for(int i = 1;i <= n;i ++){ f[i] = 1ll * cost[1][i] * i; for(int j = 1;j <= i;j ++){ f[i] = min(f[i], f[j] + 1ll * k + 1ll * cost[j+1][i]*(i-j)); } } printf("%lld ",f[n]); return 0; }