zoukankan      html  css  js  c++  java
  • POJ 3579 3685(二分-查找第k大的值)

    POJ 3579

    题意

    双重二分搜索:对列数X计算∣Xi Xj∣组成新数列的中位数

    思路

    对X排序后,与X_i的差大于mid(也就是某个数大于X_i + mid)的那些数的个数如果小于N / 2的话,说明mid太大了。以此为条件进行第一重二分搜索,第二重二分搜索是对X的搜索,直接用lower_bound实现。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    
    int N;
    int CN2 = 0;
    int X[100005];
    bool C(int x) { // 验证x作为中位数 x = X[j] - X[i] 是否太小
    	int cnt = 0;
    	for (int i = 0; i<N; ++i) {
    		cnt += N - (lower_bound(X + i, X + N, X[i] + x) - X); // 统计差值>=x的个数
    	}
    	return cnt > CN2 >> 1;
    }
    void solve() {
    	sort(X, X + N);
    	CN2 = N*(N - 1) >> 1;
    	int lb = 0, ub = 1000000001;
    	while (ub - lb > 1) {
    		int mid = (ub + lb) >> 1;
    		if (C(mid)) lb = mid; // 中位数过小,半闭半开区间[lb, ub)
    		else ub = mid;
    	}
    	printf("%d
    ", lb);
    }
    int main()
    {
    	while (scanf("%d", &N) == 1) {
    		for (int i = 0; i < N; ++i) scanf("%d", X + i);
    		solve();
    	}
    	return 0;
    }

    POJ 3685

    题意:边为n的方阵中,aij=i^2+100000i+j^2-100000j+i*j,求矩阵的第k大数。

    思路:需要注意数据规模,特别是1≤M≤N×N,需要64位变量来存

    首先来分析下这个函数f(i,j)

    image

    可知f(i,j)按行递增(同列),而按列不单调(同行)。

    C(x)表示矩阵中<x的元素有多少个。 所以可以一列一列来求有多少个比x小。然后二分求出正好有M−1个数比x小即可。

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const ll MAX = 1e15;
    ll T;
    ll N, M;
    ll f(ll i, ll j) {
    	return i*i + 100000 * (i - j) + i*j + j*j;
    }
    bool C(ll x) { // 验证x是否过小。。。。这里写成int x调了好久= =
    	ll cnt = 0; // cnt 为<x的个数
    	for (int j = 1; j <= N; ++j) {
    		int lb = 0, ub = N + 1; // (lb, ub)
    		while (ub - lb > 1) {
    			int mid = (lb + ub) >> 1;
    			if (f(mid, j) < x) lb = mid; // 半闭半开区间[lb, ub)
    			else ub = mid;
    		}
    		cnt += lb;
    	}
    	return cnt < M;
    }
    void solve() {
    	ll lb = f(0, N), ub = f(N, 0) + 1;
    	while (ub - lb > 1) {
    		ll mid = (ub + lb) >> 1;
    		if (C(mid)) lb = mid; // 半闭半开区间[lb, ub)
    		else ub = mid;
    	}
    	cout << lb << endl;
    }
    int main() {
    	cin >> T;
    	while (T--) {
    		cin >> N >> M;
    		solve();
    	}
    	return 0;
    }
  • 相关阅读:
    二十一.组合模式
    二十四.桥接模式
    二十六.职责链模式
    二十五.命令模式
    将小写转化成大写
    备份JOB SCHEDULE ENTRY的简单方法
    如何确定哪一个作业锁定QDLS下的一个目标
    WRKACTJOB命令一些有用功能介绍
    如何使用CA/400批处理的方式传输数据
    用前缀给字段命名
  • 原文地址:https://www.cnblogs.com/demian/p/7500732.html
Copyright © 2011-2022 走看看