BZOJ 1003 - 最短路 + DP
从这篇开始换字体。。
数据范围很小。。直接暴力DP之即可。。
感觉跟之前做的1597的DP很像,都是基于连续区间的DP,应该也可以用斜率优化。。
还感觉跟某次CodeVS模拟赛的题的一道变态题(多面体原谅我。。)很像。。只不过那道题最后是二分图匹配。。
题解详见代码注释。。我只想吐槽。。窝一遇到什么n m d k p都出来的题,就很容易打错变量名(又因为这WA了三四次!。。这回我一开始就写成了输出f[n](这些变量与题目意义不同...自己改了一下。。)以后,要么把变量名搞长一点、搞有意义一点;要么就再码和调的过程中小心小心再小心(简直非人哉。。
// BZOJ 1003 // 跟某道奇怪的变态的codevs月赛题(喂东西吃)很像。。 #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; const int D=100+5, N=20+5, M=N*N*2+5, P=20*100*100+5; #define rep(i,a,b) for (int i=a; i<=b; i++) #define dep(i,a,b) for (int i=a; i>=b; i--) #define read(x) scanf("%d", &x) #define fill(a,x) memset(a, x, sizeof(a)) #define LL long long int n, m, d, k, p, u, v, w, l, r; LL f[D], cost[D][D]; struct Graph { int s, from[M], to[M], pre[M], dis[M], last[N]; void init() { s=-1; fill(last, -1); } void ine(int a, int b, int w) { s++; from[s]=a, to[s]=b, pre[s]=last[a], dis[s]=w; last[a]=s; } void ine2(int a, int b, int w) { ine(a, b, w); ine(b, a, w); } } G; #define reg(i,G,u) for (int i=G.last[u]; i!=-1; i=G.pre[i]) struct List_Pair { int s, pre[P], a[P], b[P], las[N]; void init() { s=-1; fill(las, -1); } void push_back(int x, int a_, int b_) { s++; pre[s]=las[x]; a[s]=a_; b[s]=b_; las[x]=s; } } mes; #define rel(i,L,x) for (int i=L.las[x]; i!=-1; i=L.pre[i]) struct Node { int id, dis; bool operator < (const Node x) const { return dis>x.dis; } Node(int id_, int dis_) { id=id_; dis=dis_; } }; bool done[N], ok[N]; // ok[i]表示当前时间区间i点是否可走 int Dijkstra(int l, int r) { priority_queue<Node> Q; int d[N]; fill(d, 0x3f); fill(done, false); fill(ok, true); // 预处理ok[],只要当前时间区间有一天不可走,那么ok[i]就置为false rep(i,2,n-1) rel(j,mes,i) if (max(l, mes.a[j])<=min(r, mes.b[j])) { ok[i]=false; break; } Q.push(Node(1,0)); d[1]=0; while (!Q.empty()) { Node Nx=Q.top(); Q.pop(); int x=Nx.id; if (done[x]) continue; done[x]=true; reg(i,G,x) { int y=G.to[i], w=G.dis[i]; if (!done[y] && d[y]>d[x]+w && ok[y]) { d[y]=d[x]+w; Q.push(Node(y, d[y])); } } } return d[n]; } int main() { G.init(); mes.init(); scanf("%d%d%d%d", &d, &n, &k, &m); rep(i,1,m) scanf("%d%d%d", &u, &v, &w), G.ine2(u, v, w); read(p); rep(i,1,p) scanf("%d%d%d", &u, &l, &r), mes.push_back(u, l, r); // 预处理出cost[i][j]表示第i天到第j天不改变航线所需要的最小花费 // 即:当i~j天可走节点的并集可以保证能到达目的地时,则最短路跑一遍即可;否则为无穷大 rep(i,1,d) rep(j,i,d) cost[i][j]=Dijkstra(i, j); // 对花费进行DP:设f[i]表示前i天的最少花费 // 我们枚举最后一次改变路线的时间j,则有f[i] = min { f[j] + cost[j][i] } + k // 注意,还可以不更改路线,此时的花费为cost[1][i] rep(i,1,d) { f[i]=(LL)cost[1][i]*i; rep(j,1,i-1) f[i]=min(f[i], f[j]+cost[j+1][i]*(i-j)+k); } printf("%lld\n", f[d]); return 0; }