关于二分法中取中间值时向下和向上取整的问题(由大白LA3971想到的)

最近在做刘汝佳的大白,有一道题目LA_3971,也是UVA_12124,是用二分法做的。

题目地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=456&page=show_problem&problem=3276代码如下

 

/*************************************************************************
    > File Name: 12124.cpp
    > Author: BobLee
    > Mail: wustboli@gmail.com 
    > Created Time: Mon 25 Mar 2013 08:36:44 PM CST
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
using namespace std;

const int maxn = 1010;

struct co
{
	int price;
	int qua;
};

map<string,int> id;
vector<co> com[maxn];
int N,B;
int cnt;

int ID(string s)
{
	if(!id.count(s))
		id[s] = cnt++;
	return id[s];
}

bool fun(int q)
{
	int sum = 0;
	for(int i=0;i<cnt;i++)
	{
		int cheap = B+1;
		int m = com[i].size();
		for(int j=0;j<m;j++)
		{
			if(com[i][j].qua>=q)
				cheap = min(cheap,com[i][j].price);
		}
		if(cheap > B)
			return false;
		sum+=cheap;
		if(sum>B)
			return false;
	}
	return true;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&N,&B);
		char type[30],name[30];
		int price,quaa;
		int maxq = 0;
		cnt=0;
		for(int i=0;i<N;i++)
			com[i].clear();
		for(int i=0;i<N;i++)
		{
			scanf("%s%s%d%d",type,name,&price,&quaa);
			maxq = max(maxq,quaa);
			com[ID(type)].push_back((co){price,quaa});
		}
		int L=0;
		int R=maxq;
		while(L<R)
		{
			int M=(L+R+1)/2;
			//cout<<L<<"  "<<R<<" "<<M<<endl;
			if(fun(M))
			{
				L=M;
			}
			else
				R=M-1;
			//cout<<L<<"  "<<R<<" "<<M<<endl;
			//getchar();
		//	if(M==0)
		//		break;
		}
		printf("%d\n",L);
	}
	return 0;

}

可以看到我取中值的时候,用的是向上取整。(实际上是刘汝佳这么写的)

 

我当时写的是  M=(L+R)/2;   也就是向下取整的意思,但是在不同提交的遇到了一个问题。

我的TLE了。

当时我是百思不得其解,后来仔细思考了之后发现了一个问题。那就是你在这个程序中是求一个合理区间的最大值。

举个例子,假如我们的最后要取的值为5,区间也是[0,5],用向下取整的话

LR M

0 5 2

25 3

35 4

45 4

45 4

。。。

发现问题了吧,这个时候就会陷入死循环。

再来一个例子,假如我们是求一个合理区间的最小值,二分的代码

 

while(L<R)
{
        M=(L+R+1)/2;
        if(ok(M))
              R=M;
        else
              L=M+1;
}

我们还是用向上取整来做。区间为[0,5],最后的值为0

 

LR M

0 5 3

03 2

02 1

0 1 1

01 1

。。。

又陷入了死循环,但是我们可以发现如果这时用向下取整就是可行的了。

所以由上面就可以得出,

当我们使用二分法求某个合理区间最值的时候,我们要十分注意两个端点的极端值的情况。

当然还有一种更保险的方法

那就是不用上面的二分的方法

在二分的时候用另外一个变量来记录合理值,然后L=M+1  OR  R=M-1

所以这样的话是不回陷入到死循环里面去的

这就是我由这T想到了,好久不写博客了。还是要捡起来啊。


 

posted @ 2013-03-27 22:35  javawebsoa  Views(1177)  Comments(0Edit  收藏  举报