• 【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;
    }
    
    
  • 相关阅读:
    <2014 04 29> *nix环境编程常用库总结
    <2014 04 29> c/c++常用库总结
    <2014 04 26> 《Coders at Work编程人生:15位软件先驱访谈录》
    <2014 04 16> 上班实习第一天
    <2014 04 15> C++语言回顾精要(原创By Andrew)
    [荐][转]为何应该使用 MacOS X(论GUI环境下开发人员对软件的配置与重用)
    [荐][转]王垠:我和权威的故事(2014)
    [荐][转]如何用美剧真正提升你的英语水平
    [转] 数学的用处(一)(二)(三)(四)(数学图谱)
    metadata 和 routing
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21174.html
Copyright © 2020-2023  润新知