PAT甲级刷题记录——1044 Shopping in Mars (25分)

Shopping in Mars is quite a different experience. The Mars people pay by chained diamonds. Each diamond has a value (in Mars dollars M$). When making the payment, the chain can be cut at any position for only once and some of the diamonds are taken off the chain one by one. Once a diamond is off the chain, it cannot be taken back. For example, if we have a chain of 8 diamonds with values M$3, 2, 1, 5, 4, 6, 8, 7, and we must pay M$15. We may have 3 options:

  1. Cut the chain between 4 and 6, and take off the diamonds from the position 1 to 5 (with values 3+2+1+5+4=15).
  2. Cut before 5 or after 6, and take off the diamonds from the position 4 to 6 (with values 5+4+6=15).
  3. Cut before 8, and take off the diamonds from the position 7 to 8 (with values 8+7=15).

Now given the chain of diamond values and the amount that a customer has to pay, you are supposed to list all the paying options for the customer.

If it is impossible to pay the exact amount, you must suggest solutions with minimum lost.

Input Specification:

Each input file contains one test case. For each case, the first line contains 2 numbers: N (≤10​5​​ ), the total number of diamonds on the chain, and M (≤10​8​​ ), the amount that the customer has to pay. Then the next line contains N positive numbers D​1​​ ⋯D​N​​ (D​i​​ ≤10​3​​ for all i=1,⋯,N) which are the values of the diamonds. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print i-jin a line for each pair of ijsuch that Di+ … + Dj= M. Note that if there are more than one solution, all the solutions must be printed in increasing order of i.

If there is no solution, output i-jfor pairs of ijsuch that Di+ … + Dj>M with (Di+ … + Dj−M) minimized. Again all the solutions must be printed in increasing order of i.

It is guaranteed that the total value of diamonds is sufficient to pay the given amount.

Sample Input 1:

16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13

Sample Output 1:

1-5
4-6
7-8
11-11

Sample Input 2:

5 13
2 4 5 7 9

Sample Output 2:

2-4
4-5

思路

这题用自带的lower_bound()函数来做非常简单哈,但是要注意细节上的问题,我在测试点3卡了很久(大家卡的都是2和5,只有我是卡了3……晕,最后终于把问题找到了)。

首先,题目的大意就是让你求给出序列的连续子序列和能否到达M,如果能到达M,那么输出所有子序列的左右端点,并且按左端点升序排列,如果不能,那么输出大于M但值最小的那种情况的所有左右端点,同样的,也是按左端点升序排列

虽然原序列是乱序的,但是从端点1处加到端点i处的和sum[i]肯定是递增的,因此可以用二分法来优化这个问题。于是,想要求中间某段序列的和的话,只要sum[j]-sum[i-1]就行啦~(i和j分别是这段序列的左右端点)

因此,直接用lower_bound寻找,对于当前左端点i,是否存在右端点j,使其满足sum[j]=sum[i-1]+M,如果找到了,那么直接输出即可(因为左端点一定是从左往右遍历的,所以自动满足左端点升序的要求)。

如果循环结束了判断是否有可行方案的flag依然为false,就说明无解,那么就要找出大于M但是花费最小的几组解,因此,我这里选择了用结构体数组来存(好像思路比较奇葩……),只要当前解的花费大于M,那么就存进结构体数组里。最后,通过sort()函数对花费从小到大排序就好了~不过这里要注意,因为题目要求就算无解的情况,也是要按左端点升序来输出的,所以,sort()的第三个参数:cmp函数里一定要写上,如果花费相同,那么左端点小的排在前面,不然碰到相同花费的情况,可能会输出乱序(这也是我测试点3一直卡着的原因……一开始没注意到这个小细节)。

代码

#include<cstdio>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
const int maxn = 100010;
struct node{
    int x, y, cost;
}Node[maxn];
int data[maxn] = {0};
int sum[maxn] = {0};
bool cmp(node a, node b){
    if(a.cost!=b.cost) return a.cost<b.cost;
    else return a.x<b.x;
}
int main()
{
    int N, M;
    scanf("%d%d", &N, &M);
    int tmpsum = 0;
    for(int i=1;i<=N;i++){
        scanf("%d", &data[i]);
        tmpsum += data[i];
        sum[i] = tmpsum;
    }
    bool flag = false;
    for(int i=1;i<=N;i++){
        int pos = lower_bound(sum+i, sum+N+1, M+sum[i-1]) - sum;
        if(sum[pos]-sum[i-1]==M){
            flag = true;
            printf("%d-%d\n", i, pos);
        }
    }
    if(flag==false){
        int index = 0;
        for(int i=1;i<=N;i++){
            int pos = lower_bound(sum+i, sum+N+1, M+sum[i-1]) - sum;
            if(sum[pos]-sum[i-1]>M){
                Node[index].x = i;
                Node[index].y = pos;
                Node[index].cost = sum[pos]-sum[i-1];
                index++;
            }
        }
        sort(Node, Node+index, cmp);
        int minCost = Node[0].cost;
        for(int i=0;i<index;i++){
            if(Node[i].cost==minCost) printf("%d-%d\n", Node[i].x, Node[i].y);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值