抄书uva714

题意:按顺序给你N个数,将这N个数分成连续的M段,使得这M段每段的和中的最大值最小,输出最小值(1<=N<=100000,1<=M<=N,每个数在1到10000之间),如果有多种可能的话,尽量在前面进行划分。

示例:
1 2 3 2 5 4
结果:
1 2 3 | 2 5 | 4
7

想法:我对这个题的理解就是暴力,是数组分割的一个变种,先对这n个数进行m段分割,然后分别记录下一组里的和,然后max更新最大值,最后再sort排一下序,得到最小的最大值。ps:我的方法只测试了几组数据而且代码不规范错漏肯定是有的。

代码:

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
struct node {
    string str;
    int a;
    int ms[1024];
}sh[1024];
int n, arr[1024], p[1024], k, m = 0;
void dfs(int cur) {
    if (cur == n) {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += p[i];//获取标记的和,作用是固定分组数
        }
        int su = 0, ans = 0;
        if (sum == k && !p[n - 1]) {
            for (int i = 0; i < n; i++) {
                ans += arr[i];//获取每一组的数值便于计算和
                if (p[i] && i != n - 1) {
                    sh[m].str.append(to_string(arr[i]));//获取每一组值便于后期输出
                    sh[m].str.append(" ");//分隔号
                    sh[m].ms[i] = ans;//获取每一组的和
                    su = max(su, ans);//求一串数中的最大和
                //  cout << arr[i] << ",";
                    ans = 0;//分割号后从新计量下一个小分组的和
                }
                else {
                    sh[m].str.append(to_string(arr[i]));
                //  cout << arr[i] << " ";
                }
                if (i == n - 1) {
                    sh[m].ms[i] = ans;//将最后一个数放入结构体里
                    su = max(su, ans);
                }
            }
            sh[m++].a = su;
            //cout << endl;
        }
        return;
    }
    else {
            p[cur] = 1;
            dfs(cur + 1);
            p[cur] = 0;
            dfs(cur + 1);
    }
}
bool cmp(node a, node b) {//先让和从小到大排列,相同和的按照每一组的和从小到大排序
    if (a.a > b.a)return 0;
    else if (a.a == b.a) {
        int x = 0;
        while (a.ms[x] == b.ms[x++]);
        if (a.ms[x] < b.ms[x])return 1;
        else return 0;
    }
    else return 1;
}
int main(void) {
    cin >> n >> k;
    memset(p, 0, sizeof(p));
    for (int i = 0; i < n; i++)
        cin >> arr[i];
    dfs(0);
    //cout << endl;
    sort(sh, sh + m, cmp);
    cout << sh[0].a << " " << sh[0].str << endl;;
    system("pause");
    return 0;
}

在此我再贴上一份别人的代码:
原文地址:https://www.cnblogs.com/g0feng/archive/2012/10/16/2726726.html
思路:

1、由于函数具有单调性的特征,因此可以用二分枚举的办法去实现它,这与POJ3258有非常相似的地方,但这里不需要排序。

2、输出的时候需要用到贪心的思想,既尽量往前划分。

3、大概的思路就是二分枚举求得满足题意的最大值之后,然后以这个最大值通过从后往前的方式划分成段,如果剩余可划分段与i+1的值相等(尽量靠前),则将剩余的段往前划分,具体实现可以用一个标记数组use表示是否划分。

5、注意high_bound可能超int,所以需要long long 来存。

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 510;

int a[MAXN], use[MAXN];
long long  low_bound, high_bound;
int n, m;

void init()
{
    low_bound = -1;
    high_bound = 0;
    memset(use, 0, sizeof(use));
}

int solve(int mid)
{
    int sum = 0, group = 1;
    for(int i = n-1; i >= 0; i--)
    {
        if(sum + a[i] > mid)
        {
            sum = a[i];
            group++;
            if(group > m) return 0;
        }
        else sum += a[i];
    }
    return 1;
}

void print(int high_bound)
{
    int group = 1, sum = 0;
    for(int i = n-1; i >= 0; i--)
    {
        if(sum + a[i] > high_bound)
        {
            use[i] = 1;
            sum = a[i];
            group++;
        }
        else sum += a[i];
        if(m-group == i+1)
        {
            for(int j = 0; j <= i; j++)
                use[j] = 1;
            break;
        }
    }
    for(int i = 0; i < n-1; i++)
    {
        printf("%d ", a[i]);
        if(use[i]) printf("/ ");
    }
    printf("%d\n", a[n-1]);
}

int main()
{
    int T;
    scanf("%d%*c", &T);
    while(T--)
    {
        init();
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
            if(low_bound < a[i]) low_bound = a[i];
            high_bound += a[i];
        }
        long long x = low_bound, y = high_bound;
        while(x <= y)
        {
            long long  mid = x+(y-x)/2;
            if(solve(mid)) y = mid-1;
            else x = mid+1;
        }
        print(x);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值