poj3273

来源:互联网 发布:厦门广电网络 电话多少 编辑:程序博客网 时间:2024/06/06 10:06

题目还是有思维量的。大致题意为给定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 1000000010int 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;}


 

0 0