zoukankan      html  css  js  c++  java
  • 洛谷P3933 Chtholly Nota Seniorious 【二分 + 贪心 + 矩阵旋转】

    威廉需要调整圣剑的状态,因此他将瑟尼欧尼斯拆分护符,组成了一个nnnmmm列的矩阵。

    每一个护符都有自己的魔力值。现在为了测试圣剑,你需要将这些护符分成 A,B两部分。

    要求如下:

    1. 圣剑的所有护符,恰好都属于两部分中的一部分。

    2. 每个部分内部的方块之间,可以通过上下左右相互到达,而且每个内部的方块之间互相到达,最多允许拐一次弯。

    例如

    AAAAA  AAAAA  AAAAA
    AABAA  BaAAA  AAABB
    ABBBA  BBAAA  AAABB
    AABAA  BaAAA  ABBBB
    AAAAA  AAAAA  BBBBB
    
      (1)     (2)     (3)      

    其中(1)(2)是不允许的分法,(3)是允许的分法。在(2)中,a属于A区域,这两个a元素之间互相到达,没有办法最多只拐一次弯。

    现在要问,所有合法的分法中,A区域的极差与B区域的极差 中间较大的一个的 最小值 是多少?

    好心而可爱的在一旁默默观察奈芙莲悄悄地告诉你,极差就是区域内最大值减去最小值。

    输入输出格式

    输入格式:

    第一行两个自然数n,mn,mn,m

    接下来nnn行,每行mmm个自然数Ai,jA_{i,j}Ai,j表示权值

    输出格式:

    一个整数表示答案。

    输入输出样例

    输入样例#1:
    4 4
    1 12 6 11
    11 4 2 14
    10 1 9 20
    4 17 13 10
    输出样例#1:
    11

    说明

    样例解释

    1  12 6        11
    11 4  2        14
    10 1  9        20
    4        17 13 10

    分法不唯一,如图是一种合法的分法。左边部分极差12-1=11,右边一块极差20-10=10,所以答案取这两个中较大者11。没有别的分法,可以使答案更小。

    数据范围与约定

    测试点 n m
    #1-2 ≤10le 1010 ≤10le 1010
    #3-4 1 ≤2000le 20002000
    #5-7 ≤200le 200200 ≤200le 200200
    #8-10 ≤2000le 20002000 ≤2000le 20002000

    对于所有的权值1≤Ai,j≤1091le A_{i,j} le 10^91Ai,j109



    题解

    看到最大最小还是要考虑二分呐。。。虽然本蒟蒻尝试了想二分然而并不知道怎么check
    首先我们二分差值
    check的思路非常妙,由题我们知道我们选出来的A是在每一行的长度是单调且连续的,由于A和B可以互换,我们不妨设最大极值在A中,那么A这样一个梯状的区域的顶角处于某个角落,我们不妨设为左上角,然后将矩阵做3次旋转就可以考虑到所有情况。

    好了现在A的顶点在左上角,我们要判定条件是否成立。
    首先最大值和最小值一定不在一起,
    我们不妨设最大值在A中,那么我们从第一行开始找到第一个与最大值差值在check范围内的位置p
    在第二行不超过p的位置同样找一个这样的边界,
    这样子我们就将矩阵划分为了两个区域:
    左边区域一定满足条件,因为是我们枚举出来的,那么我们再判断右区域满不满足【与最小值的差值在条件范围内】
    就搞定啦~

    【get 二分特性 + 矩阵旋转 + 最大值最小值划分思想】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    using namespace std;
    const int maxn = 2017,maxm = 100005,INF = 2000000000;
    
    inline int read(){
    	int out = 0,flag = 1;char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
    	return out * flag;
    }
    
    int n,m,A[4][maxn][maxn],gmax = -INF,gmin = INF;
    
    void init(){
    	n = read();
    	m = read();
    	int x = 1,x1 = 1,x2 = n,x3 = m,y = 1,y1 = n,y2 = m,y3 = 1,t;
    	for (int i = 1; i <= n; i++){
    		for (int j = 1; j <= m; j++){
    			t = A[0][x][y++] = A[1][x1++][y1] = A[2][x2][y2--] = A[3][x3--][y3] = read();
    			if (t > gmax) gmax = t;
    			if (t < gmin) gmin = t;
    		}
    		x++; y = 1;
    		y1--; x1 = 1;
    		x2--; y2 = m;
    		y3++; x3 = m;
    	}
    }
    
    int endi[maxn];
    bool Check(int u,int d){
    	if (u & 1) swap(n,m);
    	endi[0] = m;
    	for (int i = 1,j; i <= n; i++){
    		for (j = 1; j <= endi[i - 1]; j++)
    			if (gmax - A[u][i][j] > d)
    				break;
    		endi[i] = j - 1;
    	}
    	for (int i = 1; i <= n; i++){
    		for (int j = endi[i] + 1; j <= m; j++)
    			if (A[u][i][j] - gmin > d){
    				if (u & 1) swap(n,m);
    				return false;
    			}
    	}
    	if (u & 1) swap(n,m);
    	return true;
    }
    
    bool check(int d){
    	if (Check(0,d)) return true;
    	if (Check(1,d)) return true;
    	if (Check(2,d)) return true;
    	if (Check(3,d)) return true;
    	return false;
    }
    
    void solve(){
    	int l = 0,r = gmax - gmin;
    	while (l < r){
    		int mid = (l + r) >> 1;
    		if (check(mid)) r = mid;
    		else l = mid + 1;
    	}
    	cout<<l<<endl;
    }
    
    int main()
    {
    	init();
    	solve();
    	return 0;
    }
    



  • 相关阅读:
    推送技术 --SignalR
    软件解耦
    xrBarCode 条形码的密度设置
    Javascript 中方法的重写
    数据库锁
    oracle instr,substr 截取字符串
    循环读取写入表
    Oracle For 循环,字符串拼接,查找
    iis,webservice 启用 acrobat.exe 打印
    iis,webservice 打印
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282856.html
Copyright © 2011-2022 走看看