Monthly Expense-POJ3273(最大值最小化问题,用二分法或动规解)

原题链接 Monthly Expense
题目大意:给定n,m,让你将n个数重新划分成m个数,而且是连续才能划分到一起,划分后的值为之前的这些数的和,目标是让重新划分后的这些值中最大的那个最小化。

解题思路:

1. 二分法

容易证明,分成组越少,花费越高,分成组越多,花费越少,呈线性关系,故可以采用二分法求解单调函数极值(最值)。一开始二分的上界为n天花费的总和(相当于分成1份),下界为每天花费的最大值(相当于分成n份),然后二分,每次的mid值为(上界 + 下界)/ 2,然后根据mid值遍历n天花费,对n天的花费进行累加,每当超过mid值 份数++,看看这个mid值能把n天分成几份,如果份数大于m,表示mid偏小,下界 = mid + 1,反之小于等于mid,上界 = mid - 1,然后输出最后的mid值即可,复杂度为 O(nlogM)。

#include<stdio.h>
int n,m,arr[100005],mid;
int vjude()
{
    int plane=1,sum=0;//注意这里的plane初值为1,最少一份
    for(int i=0; i<n; i++)
    {
        sum+=arr[i];
        if(sum>mid)
        {
            ++plane;
            sum=arr[i];//因为mid为最高花费,超过了就要记录到下一组
        }
        if(plane>m)
        {
            return 0;//mid低了
        }
    }
    return 1;//mid高了或者刚好合适,都需要降低mid以便找最小值
}
int main()
{
    scanf("%d%d",&n,&m);
    int low=0,high=0;
    for(int i=0; i<n; ++i)
    {
        scanf("%d",&arr[i]);
        high+=arr[i];
        if(low<arr[i])
            low=arr[i];//最高的单月花费
    }
    while(low<=high)
    {
        mid=(high+low)>>1;
        if(vjude())
            high=mid-1;
        else
            low=mid+1;
    }
    printf("%d
",low);
    return 0;
}

2. 动态规划(但由于数据规模很大,会超时)。

设best[i][j] 表示把j个单位分成i份使其中最大的一份最小的最优解的最大值,又假设第i份(最后1份)为第k+1个单位到第j个单位的和,显然前面的i-1份包括k个单位。则best[i][j] = min{max(best[i - 1][k], sum(k + 1, j))},其中k<j,sum(l, r)为l到r之间所有单位的和。

//花费-动规算法
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int f[1000][1000],n,m,sum[10000],money[10000];

// f[i][j]=min(f[i-1][k],sum[j]-sum[k])
int main()
{
    int i,j,k,minx,t,maxn;
    scanf("%d%d",&n,&m);
    for(i=1; i<=n; i++)
    {
        scanf("%d",&money[i]);
        f[1][i]=f[1][i-1]+money[i];
        sum[i]=sum[i-1]+money[i];
    }
    for(i=2; i<=m; i++)
    {
        maxn=-1;
        for(j=1; j<=i; j++)
            maxn=max(money[j],maxn);
        for(j=1; j<=i; j++)
            f[i][j]=maxn;
        for(j=i+1; j<=n; j++)
        {
            minx=9999999;
            for(k=i-1; k<j; k++)
            {
                t=max(f[i-1][k],sum[j]-sum[k]);
                minx=min(t,minx);
            }
            f[i][j]=minx;
        }
    }
    printf("%d
",f[m][n]);
    return 0;
}
原文地址:https://www.cnblogs.com/zdw20191029/p/14553387.html