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;
}

 

 


 

posted @ 2015-12-29 22:10  Armeria  阅读(214)  评论(0编辑  收藏  举报