题目还是有思维量的。大致题意为给定n个整数,要求分成m组,求出各种组合方式中每组最大的和的最小值。那么这题,最开始想到的依然是暴力。枚举所有可能的组合方式,加上剪枝,TLE。于是想用二分查找法求解单调函数。思路如下:
选定一个数x,将n个数按照每组最大x且组合个数最小的方式组合(贪心法求解对于每个x的最小组合个数)
1)如果最小组合个数大于m,则说明x的值一定小于要求的值,不满足要求
2)如果最小组合个数小于或等于m,则说明满足要求,可以组成m组,但是必须为这些数中的最小值。
而x的取值范围为min(record[i])——sum(record[i])(包括min,sum)。同时,用这些数按照上述方式组合,其最小组合个数单调递减(不严格单调递减),我们只需要用二分查找寻找出满足最小组合个数<=m的第一个数就可以了。下面是代码:
先给出dfs+剪枝代码(TLE):
#include<stdio.h>
#include<stdlib.h>
#define Max 100010
#define Maxx(a,b) (a)>(b)?(a):(b)
#define Inf 1000000010
int record[Max];
int n,m;
int Sum;
void dfs(int index,int num,int inf){
//if(inf>=Sum)
//return ;
if(index==m){
int temp=0;
for(int i=num;i<=n;i++)
temp+=record[i];
temp=Maxx(temp,inf);
if(temp<Sum)
Sum=temp;
return ;
}
int temp=0;
for(int i=num;i<=n-(m-index);i++){
temp+=record[i];
int t=Maxx(inf,temp);
if(t>=Sum)
return ;
dfs(index+1,i+1,t);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&record[i]);
Sum=Inf;
dfs(1,1,0);
printf("%d\n",Sum);
return 0;
}
下面是二分查找求单调函数代码: 524K+63MS
#include <stdio.h>
#include <stdlib.h>
#define Max 100010 //最大个数
int record[Max];
int n,m;
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
int INf=0,Sum=0;
for(int i=1;i<=n;i++){
scanf("%d",&record[i]);
if(record[i]>INf)
INf=record[i]; //求最大值
Sum+=record[i]; // 最和
}
int left=INf,right=Sum,mid; //在INf和Sum之间用二分查找法求解单调函数
while(left<=right){
mid=(left+right)>>1;
int temp=0,num=1;
for(int i=1;i<=n;i++){ //贪心法求以mid为最大上限按上述组合方式组合的最小组合个数
temp+=record[i];
if(temp>mid){
num++;
if(num>m)
break;
temp=record[i];
}
}
if(num>m) //若个数大于m,则说明mid不够大
left=mid+1;
else if(num<=m) // 求解满足要求的最小值
right=mid-1;
}
printf("%d\n",mid); // 输出最小值
}
return 0;
}