Copying Books UVA - 714 解题报告

版权声明:大鹏专属 https://blog.csdn.net/NCC__dapeng/article/details/88183979

今天开始正式开始疯狂的刷题生活!

题意:

       多组案例,大意是说先给定两个数m和k,m表示数据个数,k表示将m个数据分为k份,要求划分后的子序列的和的最大值最小,每次划分尽量往右划分。1<=k<=m<=500,子序列和的最大值不超过10000000。

       输入:首行输入案例数,次行输入m和k(使用空格符分隔),第三行输入m个数据。

       输出:输出m个数据并在分割处使用'/'间隔。
案例:

       Sample Input 

   2
   9 3
   100 200 300 400 500 600 700 800 900
   5 4
   100 100 100 100 100

     Sample Output

  100 200 300 400 500 / 600 700 / 800 900
  100 / 100 / 100 / 100 100

思路:这道题是一道经典的最大值最小化问题,而且使用二分法进行状态的枚举。

大体思路就是这样,如果不知道什么是最大值最小化也没问题,只要耐心理解这道题后就会知道。

咳咳由于我也是第一次接触这种题,所以死在了很多地方,这些地方会在代码中一一给大家指出,希望不要跟我一样**

下面 给出AC代码:

#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
#include<functional>

typedef long long ll;
using namespace std;
const int maxn=1e6+1000;
const int INF=0x3f3f3f3f;
const ll mod=1e10;
ll a[maxn];
int n,k;
bool vis[maxn];//必须用数组进行标记的存储,否则没办法控制可以自己试一下,反正我是没找到办法。

bool judge(ll x)
{
    memset(vis,0,sizeof(vis));
    ll sum=0,cnt=0;
    for(int i=n-1;i>=0;i--)
    {
        if(sum+a[i]<=x&&i+1>=k-cnt)//一定要注意这里!!!样例二中K是用不完的所以最后我们要把所有的K都是用掉,仔细理解判断条件,而且必须要从右往左划分,根据题意慢慢的来!!!与最后那段话呼应一下
        sum+=a[i];
        else
        {
            sum=a[i];//是a[i],不是0!!!
            cnt++;
            vis[i]=true;
        }
    }

    if(cnt+1>k) return false;
    else return true;
}

void print(ll t)
{
    judge(t);
    for(int i=0;i<n;i++)
    {
        if(i) printf(" %lld",a[i]);
        else printf("%lld",a[i]);
        if(vis[i]) printf(" /");

    }
    printf("\n");
}


int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&k);

        ll sum=0,mx=0;
        for(int i=0;i<n;i++)
        {
            scanf("%lld",&a[i]);
            sum+=a[i];
            mx=max(a[i],mx);//这里的mx其实有没有都可以,只是有了之后会减少时间复杂度
        }

        ll l=mx,r=sum,mid;
        while(l<r)
        {
            mid=(l+r)>>1;
            //cout<<"gg  "<<mid<<"  "<<l<<"  "<<r<<endl;
            if(judge(mid))
            {
                r=mid;
            }
            else
            {
                l=mid+1;
            }

        }

        print(r);//注意这里写的是边界r,这个与你的judge判断有关,我的judge判断等于K的时候是r去承接
    }
    return 0;

}

最后说一点,一开始这道题很快的做出来之后,纠结在了一个很傻*的问题上,那就是最后一个值大于x,会多划一道,如果最后那一组不大于x那么cnt将无法计数,就开始纠结于如何特判,消除这个影响,最后才发现是自己想歪了,这个其实用一行小代码就可以解决,我一直无法解决的原因是每次sum=0,而不是等于a[i],菜是原罪啊,还是自己的代码不行,蒟蒻蒟蒻。

猜你喜欢

转载自blog.csdn.net/NCC__dapeng/article/details/88183979