CodeForces 424D: ...(二分)

题意:给出一个n*m的矩阵,内有一些数字。当你从一个方格走到另一个方格时,按这两个方格数字的大小,有(升,平,降)三种费用。你需要在矩阵中找到边长大于2的一个矩形,使得按这个矩形顺时针行走一圈的费用,与给定费用最接近。3<=n,m<=300。

思路:O(1)计算一个矩形的费用不是什么难事,因为考虑到有前缀性质(前缀性质:[l,r] = [0,r] -
[0,l-1]),只要预处理好各行各个方向行走的费用,就容易计算。

直接枚举容易得到O(n^4)的算法。难以过。这时就应当想到优化。实际上,经过优化,可以得到O(n^3
*log(n))的算法。优化的方法如下:只枚举上下两层位置和右边界位置,正常思路是再枚举左边界位置,如果我们能二分得到左边界位置,就完美了。可惜直接二分并不满足性质。[本题关键点]这时需要构造一个前缀性质。如图

细了就不说了。思考一下吧~。然后边扫边插入前面的前缀到set里面,然后用lower_bound就可以了。不过注意边界问题。

代码:


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>

using namespace std;

#define R 0
#define L 1
#define U 2
#define D 3
#define N 400
int sum[4][N][N];
int t[3];
int mat[N][N];
int n, m, goalt;

void init() {
for (int i = 0; i < n; i++) {
sum[R][i][0] = sum[L][i][0] = 0;
for (int j = 1; j < m; j++) {
sum[R][i][j] = sum[R][i][j-1];
sum[L][i][j] = sum[L][i][j-1];
if (mat[i][j] == mat[i][j-1]) {
sum[R][i][j] += t[0];
sum[L][i][j] += t[0];
continue;
}
if (mat[i][j] > mat[i][j-1]) {
sum[R][i][j] += t[1];
sum[L][i][j] += t[2];
continue;
}
if (mat[i][j] < mat[i][j-1]) {
sum[R][i][j] += t[2];
sum[L][i][j] += t[1];
continue;
}
}
}

for (int j = 0; j < m; j++) {
sum[U][0][j] = sum[D][0][j] = 0;
for (int i = 1; i < n; i++) {
sum[U][i][j] = sum[U][i-1][j];
sum[D][i][j] = sum[D][i-1][j];
if (mat[i][j] == mat[i-1][j]) {
sum[U][i][j] += t[0];
sum[D][i][j] += t[0];
continue;
}
if (mat[i-1][j] > mat[i][j]) {
sum[U][i][j] += t[1];
sum[D][i][j] += t[2];
continue;
}
if (mat[i-1][j] < mat[i][j]) {
sum[U][i][j] += t[2];
sum[D][i][j] += t[1];
continue;
}
}
}
}

int ans[4];
int minabsdis;
typedef pair<int,int> pii;

//#define FIX 10000000
inline int getLeftVal(int iup, int idn, int j) {
//int res = -(sum[U][idn][j] - sum[U][iup][j]) + sum[R][iup][j] + sum[L][idn][j];
//printf("(%d,%d,%d) = %d\n", iup+1, idn+1, j, res);
return -(sum[U][idn][j] - sum[U][iup][j]) + sum[R][iup][j] + sum[L][idn][j];
}
void find() {
minabsdis = 999999999;
for (int iup = 0; iup < n; iup++) {
for (int idn = iup+2; idn < n; idn++) {
set< pair<int,int> > leftsum;
leftsum.clear();
set< pair<int,int> >::iterator spi;
//printf("(%d,%d)\n", iup+1, idn+1);
leftsum.insert(pii(getLeftVal(iup,idn,0), 0));
//printf("first = (%d,%d)\n", (*leftsum.begin()).first,(*leftsum.begin()).second);
for (int j = 2; j < m; j++) {
int now = sum[R][iup][j] + sum[L][idn][j] + sum[D][idn][j]-sum[D][iup][j];
int should = now - goalt;
//printf("(%d) should = %d\n", j, should);
spi = leftsum.lower_bound(pii(should, 0));
if (spi == leftsum.end()) {
//puts("meet end");
spi--;
}
else if (spi != leftsum.begin()){
int rnum = now-(*spi).first;
spi--;
int lnum = now-(*spi).first;
spi++;
if (fabs(lnum-goalt) < fabs(rnum-goalt)) {
//puts("minus");
spi--;
}
}
pii findpair = *spi;
//printf("find (%d,%d)\n", findpair.first, findpair.second);
int final = now - findpair.first;
if ((int)fabs(final-goalt) < minabsdis) {
//puts("lala");
minabsdis = fabs(final-goalt);
ans[0] = iup;
ans[1] = findpair.second;
ans[2] = idn;
ans[3] = j;
}
leftsum.insert(pii(getLeftVal(iup,idn,j-1), j-1));
}
}
}
}

int gettype(int l, int r, bool rev) {
if (rev) l^=r^=l^=r;
if (l==r) return 0;
if (l<r) return 1;
if (l>r) return 2;
}
void checkAns() {
int res = 0;
for (int j = ans[1]+1; j <= ans[3]; j++) {
res += t[gettype(mat[ans[0]][j-1], mat[ans[0]][j], false)];
res += t[gettype(mat[ans[2]][j-1], mat[ans[2]][j], true)];
}
for (int i = ans[0]+1; i <= ans[2]; i++) {
res += t[gettype(mat[i-1][ans[1]], mat[i][ans[1]], true)];
res += t[gettype(mat[i-1][ans[3]], mat[i][ans[3]], false)];
}
if ((int)fabs(res-goalt) != minabsdis) printf("error!: check:%d, output:%d\n", (int)fabs(res-goalt), minabsdis);
}

int main() {
while (scanf("%d%d%d", &n, &m, &goalt) != EOF) {
for (int i = 0; i < 3; i++) {
scanf("%d", &t[i]);
}

for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &mat[i][j]);
}
}

init();

//for (int i = 0; i < n; i++) {
// printf("%d\n", sum[U][i][0]);
//}

find();

for (int i = 0; i < 4; i++) {
printf("%d ", ans[i]+1);
}puts("");
//printf("minabsdis = %d\n", minabsdis);
checkAns();
}
return 0;
}

CodeForces 424D: ...(二分),布布扣,bubuko.com

时间: 2024-10-24 22:40:28

CodeForces 424D: ...(二分)的相关文章

CodeForces 801C 二分,浮点数

CodeForces 801C 题意: n个设备,第 i个设备每秒用电a[i],原本储存电量b[i].只有一个充电器,每秒可给一个设备充电 p.所有的设备要同时工作,问最多可以工作多长时间? tags:就是二分,但写挂了好多发.. 坑点: 1.右边界会爆1e9 ... 2.担心 double 会丢失精度,用了 long double. 然后long double ,头文件 #include <iomanip.h> .一开始用 printf("%.6Lf\n", r)输出,在

codeforces 424D

题意:给定n,m<=300的矩阵,然后求一个长宽大于2的矩形,使得是从左上角位置开始逆时针绕边框一圈的时间最少.时间的计算是:给定3个数tu,tp, td,路径上数字增加为tu,相等为tp否则为td. 思路:直接预处理出4个数组,然后直接暴力.n4的方法.但是常数小可以4000+ms过. 还有一种n3logn的方法.. 具体是固定右下角,然后枚举右上角,然后通过一个通过地推维护一个前缀,二分查找.. 不过常数肯定比暴力大估计快不了多少.. code: 1 #include <bits/stdc

codeforces 671B (二分) - xgtao -

题目链接 有n(<=500000)个人有各自的财富,Robin Hood要劫富济贫,每次把最富有的那个人的财富抢去1,加在最穷的那个人的财富里,但是一共只能抢有限次数,问最后最富有的那个人和最穷的那个人财富差的最小值. 要让最富有与最穷的差值最小,也就是要让一个最大值最小,最小值最大,用最大值最小-最小值最大那么就是最小,那么求这两个值就可以用二分. #include <iostream> #include <cstdio> #include <algorithm>

CodeForces 607A (二分)

/* ********************************************** CodeForces 607A Author:herongwei Created Time: 2016/5/31 13:00:00 File Name : main.cpp 一个线段上有n个灯塔,每个灯塔有两个属性 (位置和破坏距离) 现在一次性从右到左开启所有灯塔,每个灯塔开启后 只有该灯塔未被破坏时会破坏左边距离范围内所有灯塔, 问现在在最右边放置一个灯塔(位置和距离随意),所能破坏的最小灯塔

codeforces 359D 二分答案+RMQ

上学期刷过裸的RMQ模板题,不过那时候一直不理解>_< 其实RMQ很简单: 设f[i][j]表示从i开始的,长度为2^j的一段元素中的最小值or最大值 那么f[i][j]=min/max{d[i][j-1], d[i+2^j-1][j-1]} RMQ的ST算法: 1 void ST() //初始化 2 { 3 memset(RMQ,1,sizeof(RMQ)); 4 5 for(int i=1;i<=n;i++) 6 RMQ[i][0]=a[i]; 7 for(int j=1;(1<

Codeforces 825D 二分贪心

题意:给一个 s 串和 t 串, s 串中有若干问号,问如何填充问号使得 s 串中字母可以组成最多的 t 串.输出填充后的 s 串. 思路:想了下感觉直接怼有点麻烦,要分情况:先处理已经可以组成 t 串的部分,然后处理 s 串中可以利用的部分,如果还有问号剩余,再直接怼.但如果用二分写就很直观了,直接看最多能组成多少个 t 串. 居然踩了long long的坑感觉自己宛若一个zz.check函数中的计算要用long long防止溢出= =(明明大水题的说. #include<iostream>

Codeforces 460C 二分结果+线段树维护

发现最近碰到好多次二分结果的题目,上次多校也是,被我很机智的快速过了,这个思想确实非常不错.在正面求比较难处理的时候,二分结果再判断是否有效往往柳暗花明. 这个题目给定n个数字的序列,可以操作m次,每次要操作w个连续的数字,每次的操作将使得该段连续数字的数都+1,最后求整个序列最小值的最大值 求最小值最大,明显的二分结果的题目,我一开始还是在ACdream那个群里看到这个题,说是二分+线段树的题目,我就来做了一下..首先二分部分很容易,下界就是初始序列的最小值,上界就是 下界+m,至于怎么判断这

CodeForces 460C——二分+前缀和—— Present

Little beaver is a beginner programmer, so informatics is his favorite subject. Soon his informatics teacher is going to have a birthday and the beaver has decided to prepare a present for her. He planted n flowers in a row on his windowsill and star

Codeforces 614D 二分

D. Skills 题意: 给出 n, A, cf, cm, m,表示有 n 个技能,每个技能当前水平为 a[i] ,最高水平为 A ,有 m 个技能点,花费一个技能点可以使任意一个技能水平加一 (最高只能是 A). 如果最后水平为 A 的技能有 x 个,最低的技能水平值为 y,定义权值为 cf*x+cm*y .求可能的最高权值,并输出最后每个技能的水平. tags: 枚举 x ,对当前 x 二分可能的最低水平值,check 时还要再二分一下. // 614D #include<bits/std