team2012-F3-sol-0023

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

{{{
题意:给定n个数组成的一个序列,要选出m个特殊位置,使得剩下的所有数到距其最近的特殊位置之和最小,如果有多种方案取字典序最小的
然后要使得所有数移到最近的特殊位置,求最小的容量。
解法:由题意,选取特殊位置的构造法必然要将这n个数划分成m个组:
1,取一个最大奇数k,使得 k * m <= n,先给每个组放k个数
2,若余下 r = n - k * m,则从后往前,每个组放2个
3,若余下一个,则放在那个组的末尾
最后形如:
**_** **_** ***_*** ***_*** 或者 **_** **_*** ***_*** ***_***
在第一种情况里,有一个位置到两边都存在最近距离的,要特殊记录一下
然后二分容量,从左到右贪心地测试即可
}}}
{{{
#include <iostream>
#include <cstdio>
using namespace std;

int a[100004], group[100004], spg, m, n;

bool test(int c)
{
    int p = 0, sum = 0, i, j;
    for(i=1;i<=m;i++)
    {
        for(j=1;j<=group[i];j++)
        {
            sum += a[p+j];
            if(sum > c)
                return 0;
        }
        if(i == spg)
        {
            sum += a[p+j];
            if(sum > c)
                sum = 0;
            else
                sum = -a[p+j];
        }
        else
            sum = 0;
        p += group[i];
    }
    return 1;
}

int gao()
{
    int low = 0, high = 100000002;
    while(low < high)
    {
        int mid = (low+high) / 2;
        if(test(mid))
            high = mid;
        else
            low = mid + 1;
    }
    return low;
}

int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        for(int i=1;i<=n;i++)
            scanf("%d", a+i);

        int com = n / m;
        if(com%2 == 0)
            com--;
        for(int i=1;i<=m;i++)
            group[i] = com;
        int rest = n - com*m;
        if(rest%2 == 0)
        {
            rest /= 2;
            for(int i=m;i>m-rest;i--)
                group[i]+=2;
            spg = m-rest;
        }
        else
        {
            rest /= 2;
            for(int i=m;i>m-rest;i--)
                group[i]+=2;
            group[m-rest]++;
            spg = -1;
        }

        printf("%d\n", gao());
    }
    return 0;
}
}}}
题意:给定n个数组成的一个序列,要选出m个特殊位置,使得剩下的所有数到距其最近的特殊位置之和最小,如果有多种方案取字典序最小的
然后要使得所有数移到最近的特殊位置,求最小的容量。
解法:由题意,选取特殊位置的构造法必然要将这n个数划分成m个组:
1,取一个最大奇数k,使得 k * m <= n,先给每个组放k个数
2,若余下 r = n - k * m,则从后往前,每个组放2个
3,若余下一个,则放在那个组的末尾
最后形如:
**_** **_** ***_*** ***_*** 或者 **_** **_*** ***_*** ***_***
在第一种情况里,有一个位置到两边都存在最近距离的,要特殊记录一下
然后二分容量,从左到右贪心地测试即可
#include <iostream>
#include <cstdio>
using namespace std;
int a[100004], group[100004], spg, m, n;
bool test(int c)
{
    int p = 0, sum = 0, i, j;
    for(i=1;i<=m;i++)
    {
        for(j=1;j<=group[i];j++)
        {
            sum += a[p+j];
            if(sum > c)
                return 0;
        }
        if(i == spg)
        {
            sum += a[p+j];
            if(sum > c)
                sum = 0;
            else
                sum = -a[p+j];
        }
        else
            sum = 0;
        p += group[i];
    }
    return 1;
}
int gao()
{
    int low = 0, high = 100000002;
    while(low < high)
    {
        int mid = (low+high) / 2;
        if(test(mid))
            high = mid;
        else
            low = mid + 1;
    }
    return low;
}
int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        for(int i=1;i<=n;i++)
            scanf("%d", a+i);
        int com = n / m;
        if(com%2 == 0)
            com--;
        for(int i=1;i<=m;i++)
            group[i] = com;
        int rest = n - com*m;
        if(rest%2 == 0)
        {
            rest /= 2;
            for(int i=m;i>m-rest;i--)
                group[i]+=2;
            spg = m-rest;
        }
        else
        {
            rest /= 2;
            for(int i=m;i>m-rest;i--)
                group[i]+=2;
            group[m-rest]++;
            spg = -1;
        }
        printf("%d\n", gao());
    }
    return 0;
}