• 【ybtoj高效进阶 21174】景区旅行(二分)(倍增)(状压DP)(DP)


    景区旅行

    题目链接:ybtoj高效进阶 21174

    题目大意

    给你一个无向图,边有贡献,然后你有一个油量,每走一条边油量减一,然后总贡献加上边的贡献。
    然后你的油量不能是负数,你可以在一些地方加油,你有油量上限,每个地方也有能加到的油量,你的油量会变成这两个的最小值,然后每个地方加油也有对于的费用。
    然后多次询问,每次告诉你出发点,要的总贡献和有的钱,然后问你要至少要有那么多的总贡献,最多能省下多少钱。
    (如果用所有钱都没有那么多贡献就输出 -1)

    思路

    首先我们考虑不加油,给出初始有的油和起点终点,问你最大贡献。

    这个可以通过倍增加状压 DP 之类的玩意儿快速实现。
    然后你会发现你就可以通过另外的 DP 求出起点是 (i),用的钱恰好是 (j) 能有的最大贡献。

    然后不难看出搞个这个的前缀和,我们就可以二分出最少要用的钱数。
    然后就有答案了。

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define ll long long
    
    using namespace std;
    
    int n, m, C, T, v[101], u[101], x, y;
    ll z, go[101][101][21], f[101][10001];
    ll ww[101], w[101][101];
    
    int main() {
    //	freopen("trip.in", "r", stdin);
    //	freopen("trip.out", "w", stdout);
    	
    	scanf("%d %d %d %d", &n, &m, &C, &T);
    	for (int i = 1; i <= n; i++) scanf("%d %d", &v[i], &u[i]);
    	
    	memset(go, -1, sizeof(go));
    	for (int i = 1; i <= n; i++) go[i][i][0] = 0;
    	for (int i = 1; i <= m; i++) {
    		scanf("%d %d %lld", &x, &y, &z);
    		go[x][y][0] = max(go[x][y][0], z);
    //		go[y][x][0] = max(go[y][x][0], z);
    	}
    	
    	for (int d = 1; d <= 20; d++)//倍增
    		for (int i = 1; i <= n; i++)
    			for (int j = 1; j <= n; j++) {
    				go[i][j][d] = go[i][j][d - 1];
    				for (int k = 1; k <= n; k++)
    					if (go[i][k][d - 1] != -1 && go[k][j][d - 1] != -1)
    						go[i][j][d] = max(go[i][j][d], go[i][k][d - 1] + go[k][j][d - 1]);
    			} 
    	memset(w, -1, sizeof(w));
    	for (int i = 1; i <= n; i++) {//状压 DP
    		int noww = min(C, u[i]);
    		w[i][i] = 0;
    		for (int j = 20; j >= 0; j--)
    			if (noww & (1 << j)) {
    				noww -= (1 << j);
    				for (int k = 1; k <= n; k++) ww[k] = w[i][k];
    				for (int k = 1; k <= n; k++)
    					for (int l = 1; l <= n; l++)
    						if (ww[k] != -1 && go[k][l][j] != -1)
    							w[i][l] = max(w[i][l], ww[k] + go[k][l][j]);
    	
    			}
    	}
    	
    	memset(f, -1, sizeof(f));//普通 DP
    	for (int j = 0; j <= n * n; j++)
    		for (int i = 1; i <= n; i++) {
    			if (j < v[i]) f[i][j] = 0;
    				else {
    					for (int k = 1; k <= n; k++)
    						if (f[k][j - v[i]] != -1 && w[i][k] != -1)
    							f[i][j] = max(f[i][j], f[k][j - v[i]] + w[i][k]);
    				}
    		}
    	
    	for (int j = 1; j <= n * n; j++)
    		for (int i = 1; i <= n; i++) {
    			f[i][j] = max(f[i][j], f[i][j - 1]);
    		}
    	while (T--) {
    		scanf("%d %d %lld", &x, &y, &z);
    		int l = 0, r = y, ans = -1;
    		while (l <= r) {//二分
    			int mid = (l + r) >> 1;
    			if (f[x][mid] >= z) ans = mid, r = mid - 1;
    				else l = mid + 1;
    		}
    		if (ans == -1) printf("-1
    ");
    			else printf("%d
    ", y - ans);
    	}
    	
    	return 0;
    }
    
    
  • 相关阅读:
    真正的e时代
    在线手册
    UVA 10616 Divisible Group Sums
    UVA 10721 Bar Codes
    UVA 10205 Stack 'em Up
    UVA 10247 Complete Tree Labeling
    UVA 10081 Tight Words
    UVA 11125 Arrange Some Marbles
    UVA 10128 Queue
    UVA 10912 Simple Minded Hashing
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21174.html
Copyright © 2020-2023  润新知