zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    JVM 常量池、运行时常量池、字符串常量池
    JVM Direct Memory
    JVM 方法区
    JVM GC Roots
    jvm 堆
    jvm slot复用
    JVM 虚拟机栈
    JVM 程序计数器
    java打印树形目录结构
    java 通过反射获取数组
  • 原文地址:https://www.cnblogs.com/zdw20191029/p/14553387.html
Copyright © 2011-2022 走看看