2015 Asia BeiJing Regional Contest A. Xiongnu's Land(二分)

15 篇文章 0 订阅
8 篇文章 0 订阅


题意:在一个二维坐标系中,给定一个矩形陆地,左下角坐标为 (0, 0)、右上角为 (R, R),在陆地中有 N 个矩形状的不重叠的绿洲,给出每个绿洲的左上角坐标 (L, T)、以及宽度 W 和高度 H。现在要你用 x = n ( n 为整数) 的一条竖线把整个陆地分成左 (left) 右 (right) 两部分,满足以下两个条件:

1)左边绿洲的总面积大于等于右边绿洲的总面积,并且绿洲面积差应尽可能小;

2)在满足第一个条件下,尽可能使得左边陆地的面积尽可能大。

分析:二分。在二分的过程中(我是左开右闭),如果左边绿洲面积等于右边绿洲面积,那么就要考虑当前 x = mid 以及 x = mid + 1 是否穿过了某些绿洲,如果穿过了的话,可想而知,该mid值即是结果,直接return mid即可,否则的话就按照二分的规则进行。这样得到最后结果是区间右边界 r,这里又是一个容易出错的地方,如果 x = r 的右边并没有任何绿洲的存在,那么应该得到的结果是 R,否则才是 r。这样子即可得到正解。

根据二分的性质,如果左边绿洲面积大于右边的时候,是一直减小右边界的。这里存在一种情况,如果最终结果就是左边绿洲面积大于右边,并且x = r 没有穿过任何绿洲,但是它右边连续的一段陆地并没有绿洲存在,那么结果应该往右移直到碰到一个绿洲结束,当然也有可能直到R。

题目链接:hihoCoder #1249

代码清单:

/*******************************************************************************
 *** problem ID  : A_Xiongnu's_Land.cpp
 *** create time : Sun Nov 15 21:41:53 2015
 *** author name : nndxy
 *** author blog : http://blog.csdn.net/jhgkjhg_ugtdk77
 *** author motto: never loose enthusiasm for life, life is to keep on fighting!
 *******************************************************************************/

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <ctime>
#include <vector>
#include <cctype>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>

using namespace std;

#define exit() return 0

typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;

const int maxn = 10000 + 5;

struct oasis { int L, T, W, H; };

int K;
int R;
int N;
oasis oa[maxn];
ll sumoa;

void input(){
	sumoa = 0;
	scanf("%d", &R);
	scanf("%d", &N);
	for(int i = 0; i < N; i++){
		scanf("%d%d%d%d", &oa[i].L, &oa[i].T, &oa[i].W, &oa[i].H);
		sumoa += ((ll)oa[i].W * (ll)oa[i].H);
	}
}

ll getleft(int mid){ // 左边绿洲面积
	ll ret = 0;
	for(int i = 0; i < N; i++){
		if(oa[i].L >= mid) break;
		else if(oa[i].L + oa[i].W <= mid) ret += ((ll)oa[i].W * (ll)oa[i].H);
		else ret += ((ll)(mid - oa[i].L) * (ll)(oa[i].H));
	}return ret;
}

bool check(int mid){ // x = mid 是否穿过绿洲
	for(int i = 0; i < N; i++){
		if(oa[i].L < mid && oa[i].L + oa[i].W > mid) return true;
	}return false;
}

bool cmp(oasis a, oasis b) { return a.L < b.L; }

int work(){

	sort(oa, oa + N, cmp);

	int l = 0, r = R, mid;
	while(l + 1 < r){ //   (l, r]
		mid = (l + r) >> 1;
		ll Left = getleft(mid);
		ll Right = sumoa - Left;
		if(Left < Right) l = mid;
		else if(Left == Right){
			if(check(mid) || check(mid + 1)) return mid;
			else l = mid;
		}
		else r = mid;
	}
	
	//当 x = r 穿过了绿洲
	if(check(r)) return r;

	for(int i = 0; i < N; i++){
		if(oa[i].L >= r) return oa[i].L;
	}
	
	return R;
}

void solve(){
	printf("%d\n", work());
}

int main(){
	scanf("%d", &K);
	while(K--){
		input();
		solve();
	}   exit();
}






评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值