zoukankan      html  css  js  c++  java
  • BZOJ 3232 圈地游戏 (分数规划 + SPFA找负/正环)

    题意

    BZOJ 3232

    题解

    对于这种A/BA/B的最值问题一般都是用分数规划解决。

    假设A/BA/B最大值为gg

    A/BgABg0A/Ble g o A-Bgle 0

    那么我们二分一个midmid

    • 如果maxABmid>0max{A-Bcdot mid}>0,说明mid<gmid<g
    • 如果maxABmid<0max{A-Bcdot mid}<0,说明mid>gmid>g
    • 如果maxABmid=0max{A-Bcdot mid}=0,说明mid=gmid=g

    那么问题转换为求maxABmidmax{A-Bcdot mid}

    对于这道题,发现合法路径是一个环,那么我们把边的权值乘上mid-mid,那么就是求是否存在边权加上围成图形内的点权为正的环。找正环就SPFASPFA就行了。

    考虑如何记录围成的点权和。

    我们给环顶一个方向,逆时针。那么我们向上走就加上这一行左边的前缀和,向下走就减去这一行左边的前缀和。同时路径上的边权要加。那么这样形成一个环恰好就包含了中间的点权和。

    CODE

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    template<class T>inline void read(T &x) {
    	char ch; while(!isdigit(ch=getchar()));
    	for(x=ch-'0';isdigit(ch=getchar());x=x*10+ch-'0');
    }
    typedef long long LL;
    const int MAXN = 2650;
    const int MAXE = (MAXN<<1)<<1;
    const int inf = 1000000000;
    int n, m, id[55][55], xx[MAXN], yy[MAXN], tim[MAXN]; //tim存进队次数
    double a[55][55], l[55][55][4], dis[MAXN];
    //l[i][j][k]存从(i,j)往k方向走的边权
    //a[i][j]存(i,j)这一行往左的和
    queue<int>q;
    bool inq[MAXN];
    const int dx[4] = { 1, -1, 0, 0 };
    const int dy[4] = { 0, 0, 1, -1 };
    bool chk(double g) {
    	for(int i = 1; i <= (n+1)*(m+1); ++i) dis[i] = -inf, tim[i] = 0, inq[i] = 0;
    	//记得清零inq,本来spfa不用清零,但是这里有中途退出所以要清零
    	while(!q.empty()) q.pop();
    	q.push(id[1][1]); dis[id[1][1]] = 0;
    	while(!q.empty()) {
    		int U = q.front(); q.pop(); inq[U] = 0;
    		int u = xx[U], v = yy[U]; double W;
    		for(int k = 0, x, y, V; k < 4; ++k) {
    			x = u + dx[k], y = v + dy[k];
    			if(x >= 1 && y >= 1 && x <= n+1 && y <= m+1) {
    				V = id[x][y]; W = -g*l[u][v][k];
    				if(k == 0) W += -a[u][v-1];
    				if(k == 1) W += a[u-1][v-1];
    				if(dis[V] < dis[U] + W) {
    					dis[V] = dis[U] + W;
    					if(++tim[V] > (n+1)*(m+1)) return 1;
    					if(!inq[V]) inq[V] = 1, q.push(V);
    				}
    			}
    		}
    	}
    	return 0;
    }
    int main () {
    	read(n), read(m);
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1; j <= m; ++j)
    			read(a[i][j]), a[i][j] += a[i][j-1];
    	for(int i = 1; i <= n+1; ++i)
    		for(int j = 1, x; j <= m; ++j) {
    			read(x);
    			l[i][j][2] = l[i][j+1][3] = x;
    		}
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1, x; j <= m+1; ++j) {
    			read(x);
    			l[i][j][0] = l[i+1][j][1] = x;
    		}
    	for(int i = 1; i <= n+1; ++i)
    		for(int j = 1; j <= m+1; ++j) {
    			id[i][j] = (i-1)*(m+1) + j; //编号
    			xx[id[i][j]] = i; //存坐标
    			yy[id[i][j]] = j;
    		}
    	double l = 0, r = 100.0*n*m, mid;
    	while(r-l>1e-5) {
    		mid = (l+r)/2;
    		if(chk(mid)) l = mid;
    		else r = mid;
    	}
    	printf("%.3f
    ", l);
    }
    
  • 相关阅读:
    菜鸡学习之路之并查集
    Leetcode 28. 实现 strStr() python3
    Leedcode 67. 二进制求和 python3
    2020 高校战“疫”网络安全分享赛 misc ez_mem&dump & 隐藏的信息
    leetcode 709.转换成小写字母
    2020 MetasequoiaCTF 部分misc
    Linux任务在后台运行
    Linux网络监控(netstat)
    Linux中wget资源下载
    Linux远程登录+远程发送文件
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039176.html
Copyright © 2011-2022 走看看