ALDS1_4_D:搜索的应用---计算最优解(二分法的应用)

题面

You are given n packages of wi kg from a belt conveyor in order (i=0,1,…n−1). You should load all packages onto k trucks which have the common maximum load P. Each truck can load consecutive packages (more than or equals to zero) from the belt conveyor unless the total weights of the packages in the sequence does not exceed the maximum load P.

Write a program which reads n, k and wi, and reports the minimum value of the maximum load P to load all packages from the belt conveyor.

Input
In the first line, two integers n and k are given separated by a space character. In the following n lines, wi are given respectively.

Output
Print the minimum value of P in a line.

Constraints
1≤n≤100,000
1≤k≤100,000
1≤wi≤10,000
Sample Input 1
5 3
8
1
7
3
9
Sample Output 1
10
If the first truck loads two packages of {8,1}, the second truck loads two packages of {7,3} and the third truck loads a package of {9}, then the minimum value of the maximum load P shall be 10.

Sample Input 2
4 2
1
2
2
6
Sample Output 2
6
If the first truck loads three packages of {1,2,2} and the second truck loads a package of {6}, then the minimum value of the maximum load P shall be 6.

思路

确定最大运载量P时,先编写一个函数求k辆车以内能运载货物的最大个数,根据题意,货物是依次出现的所以一定是按照输入顺序上车的(我一开始没搞清楚这里的题意,对货物的重量w数组sort了一下,然后怎么调都调不对)。如果P状态下能装下>=n个货物,则返回n,否则返回当前装货物的最大数i

int cont(long long p){
    int t=k,i=0;
    long long tt=0;
    while (t--){
    	while (tt<=p){
    		  tt+=w[i++];
    		  if (i-1==n && tt<=p){
    		  	// if (t==0){
    		  	    return n;	
			//	 }
				// else return n+1;
			  }
			  if (tt>p) i--;
		}
		tt=0;
	}
	return i;
}

然后答案就是第一使得cont(p)==n时候的P。这个P显然需要使用搜索算法在[min(w[i]),sum(w[i])]这个范围内找出来(左边界是所有货物里最轻的那个,右边界是所有货物重量的总和(也就是只有一辆车全装的情况)。由于这里1≤n≤100,000
1≤wi≤10,000,用线性搜索显然是不靠谱的,所以用二分搜索法。搜索范围就是[min(w[i]),sum(w[i])+1](二分法右边界指向末位元素后一位置),此时时间复杂度为O(nlogP)。
这里改装二分搜索模板的思路如下:
因为中止条件是可装货物个数v>=n,所以当v<n时(k辆车装不下那么多货时候),P需要大一点,所以找[mid+1,r],v>=n时仅调整l=mid,不做任何处理(不用去判断是否是第一个达到条件的)等全部循环结束后,输出的右边界一定是第一个达到条件的那个p,也就得到了正解

代码

#include<bits/stdc++.h>
using namespace std;
int w[100001];
int n,k,v;
int cont(long long p){
    int t=k,i=0;
    long long tt=0;
    while (t--){
    	while (tt<=p){
    		  tt+=w[i++];
    		  if (i-1==n && tt<=p){
    		  	// if (t==0){
    		  	    return n;	
			//	 }
				// else return n+1;
			  }
			  if (tt>p) i--;
		}
		tt=0;
	}
	return i;
}
int main(){
	cin>>n>>k;
	long long l=20000,r=1,mid;
	for (int i=0; i<n; i++){
		cin>>w[i];
		r+=w[i];
		if (w[i]<l) l=w[i];
	}
	while (l<r){
		  mid=(l+r)/2;
		  v=cont(mid);
		  //if (cont(mid)==n){
		//  	  while (cont(mid)==n) mid--;
		 // 	  cout<<mid+1<<endl;
		//  	  break;
		//  }
		  if (v<n)
		     l=mid+1;
		  else
		     r=mid;
	}
	cout<<r<<endl;
	return 0;
}

(说实话其实这章最难的不是这道例题,而是散列表…)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值