2011年长沙市小学生信息学奥林匹克竞赛决赛 长跑接力

87 篇文章 0 订阅

Description

长跑接力赛全程m公里,规定:每个队5人,每个人都必须跑而且只能跑一次,并且至少跑1公里、最多跑n公里,接力点必须在整公里处。刘教练挑选了5名队员,测试后得到每个人连续跑1、2、3、……、n公里的最短时间。他准备精心安排每个队员跑的公里数,使全队完成接力赛用时最短。你能帮教练做一个最佳方案吗?(数据保证最佳方案唯一)
(特别注意:每人连续跑的路程越长速度越慢,若有保持速度的,也绝不会变快。)

Input

m n(m<=5000,n<=1000)
下接5行,每行n个整数(表示每人连续跑1-n公里的最短时间,以空格相隔)

Output

第一行:最短时间(时间<=maxlongint)
第二行:五个整数(表示安排1~5号队员各自连续跑的公里数,以空格相隔)

Sample Input

25 10
333 700 1200 1710 2240 2613 3245 3956 4778 5899
300 610 960 1370 1800 2712 3834 4834 5998 7682
298 612 990 1560 2109 2896 3790 4747 5996 7654
289 577 890 1381 1976 2734 3876 5678 6890 9876
312 633 995 1467 1845 2634 3636 4812 5999 8123

Sample Output

9748
6 5 5 4 5

[数据范围]

对于50%的数据,n<=500,m<=2500

对于100%的数据,n<=1000,m<=5000

分析

我们可以知道,

一个人无论怎样跑,他所贡献的价值只是与他跑的总距离相关。

就这一点,我们就可以想到一个可以骗分的方法。

50%做法

我们就下一公里来讨论。

假设每第i个人已经跑 ansi 公里,

那么他们下一公里就要 ai,ansi+1ai,ansi

运用贪心的思路,

对于这一公里就选一个时间最小的就可以了。

  • 但是这种做法有一个问题, 如果这一公里某个人需要很少时间,不过在下一公里的时候就需要非常多的时间,而另外一个人连续跑两公里比两个人分别跑一公里更少时间。

所以,我们需要换一个思路。

100%做法

因为人数很少,而且距离不是太大,

所以我们考虑DP。

fi,j 表示现在是第i个人跑完了,总共跑了j公里。

转移显然。

枚举第i个人跑多少公里,

设他跑了k公里,k的取值范围自己想一下。

fi,j=fi1,jk+ai,k

这样,我们就可以求出了最少时间了。

  • 那么怎样求每个人跑了多少?

我们可以开多一个数组,

用了记录当前状态 fi,j 是通过哪一个 fi,j 转移过来的,

其实只需要记录跑的公里数就可以了。

code(c++)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <stdlib.h>
#include <math.h>
using namespace std;

int m,n,a[8][1003],g[8][5003],f[8][5003],mx,w,time,ans[8];

int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=5;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&a[i][j]);

    memset(f,127,sizeof(f));
    f[0][0]=0;

    for(int i=1;i<=5;i++)
        for(int j=i;j<=m;j++)
        {
            for(int k=1;k<=min(n,j-i+1);k++)
                if(f[i][j]>f[i-1][j-k]+a[i][k])
                {
                    f[i][j]=f[i-1][j-k]+a[i][k];
                    g[i][j]=k;
                }
        }

    printf("%d\n",f[5][m]);
    for(int i=5;i;i--)
    {
        ans[i]=g[i][m];
        m-=g[i][m];
    }
    for(int i=1;i<=5;i++)
        printf("%d ",ans[i]); 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值