公共交通问题

现在的大学生刚毕业不久如果没有家庭的支持一般是买不起心仪的车的,所以刚毕业的人一般都要乘公交车上下班,在早晚的上下班高峰时间段,道路交通很拥挤,每站都有人上下,公交车在每站都停。刚刚毕业的小明常常会被每站都停的公交车弄得很不耐烦,于是他提出了这样一个办法:

由于公交车的站点并不是非常多,那么在繁忙的上下班高峰时间,每次公交车从始发站点往终点站点开时,我们只允许公交车停在其中的某一个站点。所有乘客都从始发站点上公交车,到达某站点后,公交车停下来,所有乘客再从这里步行到自己的目的站点。在始发站的时候,每个乘客选择自己的目的站点,公交车系统则自动计算出应停的站点。

现在请问:公交车停在哪一站点能够保证这次乘坐公交车的所有乘客步行的站点数之和最少?

在这里为了更好体现算法的魅力所在,我们假定站点数可以很多,每次乘客人数也可以很多(实际生活中这两点是不可能出现的)。另外为了方便起见,在这里我们规定,刚开始公交车在始发站,并且认为乘客至少是在第一个站点及以后才有可能下车。

不过在现实生活中,公交车只停靠一个站点实在是太不人性化了,如果公交车可以停靠K个站点而不是一个站点,那么现在的问题变成:公交车停靠在哪K个站点能够保证这次乘坐公交车的所有乘客步行的站点数之和最小?

Input
有两行,第一行是两个正整数公交站点数N(1<=N<=200)和K(1<=K<=100,K<=N),第二行是N个正整数,分别表示对应站点(以该站点作为目的站点)的乘客人数M(1<=M<=1000)。

Output
也有两行,第一行输出一个正整数,即这次乘坐公交车的所有乘客步行的站点数之和的最小值,第二行是计算出来的公交车停靠的K个站点,站点之间有一个空格。如果有多组答案,那么输出的是K个站点值分别在相同答案中是最小的,而且规定输出的顺序是从小到大。

Sample Input
5 2
14 12 2 18 15
6 3
4 9 0 16 16 12

Sample Output
29
1 4
16
2 4 5

Hint
我们在这里是规划具体停靠的公交站点,如果坐了公交车,有乘客发现自己走的站数大于不坐车而直接走的站数,我们认为也要先坐车后再步行。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
const long long INF=(1LL<<60);
long long dp[MAXN][MAXN],a[MAXN],sum[MAXN][MAXN];
int nex[MAXN][MAXN],n,k;
long long get_sum(int pos,int l,int r)
{
    return sum[pos][r]-sum[pos][l-1];
}
int main()
{
    while(scanf("%d %d",&n,&k)!=EOF)
    {
        for(int i=1;i<=n;++i)
        {
            scanf("%lld",&a[i]);
        }
        for(int i=0;i<=n+1;++i)
        {
            for(int j=0;j<=k+1;++j)
            {
                dp[i][j]=INF;
            }
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                sum[i][j]=sum[i][j-1]+a[j]*abs(i-j);
            }
        }
        for(int i=n;i;--i)
        {
            dp[i][k]=get_sum(i,i,n);
        }
        for(int j=k-1;j;--j)
        {
            for(int i=1;i<=n;++i)
            {
                for(int p=i+1;p<=n;++p)
                {
                    int mid=(i+p)>>1;
                    if(dp[i][j]>dp[p][j+1]+get_sum(i,i,mid)+get_sum(p,mid+1,p))
                    {
                        dp[i][j]=dp[p][j+1]+get_sum(i,i,mid)+get_sum(p,mid+1,p);
                        nex[i][j]=p;
                    }
                }
            }
        }
        for(int i=1;i<=n;++i)
        {
            dp[i][1]+=get_sum(i,1,i);
        }
        long long ans=INF;
        int anspos;
        for(int i=1;i<=n;++i)
        {
            if(ans>dp[i][1])
            {
                ans=dp[i][1];
                anspos=i;
            }
        }
        printf("%lld\n",ans);
        for(int i=1;i<=k;++i)
        {
            printf("%d%c",anspos,i==k?'\n':' ');
            anspos=nex[anspos][i];
        }
    }
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值