1044 Shopping in Mars (25分)[二分][模拟]

Write by Jalan

题干

火星人用一串钻石链付款,钻石链切下来之后不能复原
给你一串钻石链上面是他们的价值代码,和一个价格,你需要找出所有的支付方法,如果没有可能准确支付的时候找花费最小的方法。

输入格式

第一行两个数一个N<=105是钻石总数M<=108顾客需要支付的数额
下一行给N个数字每个<=10^3是钻石的价值用空格分开,下标是1-N

输出格式

保证钻石的总价值对于支付是充足的,如果能准确支付,打印i-j角标,多个的时候按i升序.不能准确支付打印使超出最小的i-j也是按i升序.

题解

第一次(2,5测试点超时)

思路

对整个串的每个元素进行循环.
保存现有的最小差值和每个元素进行循环时的和,以及输出存档.
如果和和价值M的差值比现有最小差值小,那么清空输出存档,向输出存档里加入这次的数据.
如果和和价值M的差值和现有最小差值相等,那么向输出存档里加入这次的数据.
如果和和价值M的差值比现有最小差值大,什么都不做,结束循环.

预期时间复杂度

n^2

代码

C++
#include <cstdint>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include<cstdio>
#include <iostream>
typedef struct pairr
{
    int i;
    int j;
}pairr;
using namespace std;


int main(int argc, char const *argv[])
{
    //input
    int N,M;
    scanf("%d %d",&N,&M);
    int *chain=(int *)malloc(sizeof(int)*N+5);
    for (int i = 1; i <= N; i++)
    {
        scanf("%d",chain+i);
    }
    
    //process
    int min = INT32_MAX;
    vector<pairr> out;
    int this_turn_count=0;
    for (int k = 1; k <=N; k++)
    {
        this_turn_count=0;
        for (int l = k; l <= N; l++)
        {
            this_turn_count+=chain[l];
            if (this_turn_count>=M)//足够支付的情况下
            {
                while (this_turn_count-chain[k]>=M)
                {
                    this_turn_count-=chain[k];
                    k++;
                }//小优化,把前面可以裁掉的部分裁掉.
                if (this_turn_count-M<min)//比当前差比最小差小的时候清空存档加入当前,循环终止
                {
                    out.clear();
                    min = this_turn_count - M;
                    out.push_back({k, l});
                    break;
                }
                if (this_turn_count-M==min)//当前差等于最小差的时候当前对加入存档,循环中缀
                {
                    out.push_back({k, l});
                    break;
                }
                //比当前差比最小差大的时候什么都不做,循环终止
                break;
            }
        }
    }
    
    //output
    for (int i = 0; i < out.size(); i++)
    {
        printf("%d-%d\n", out[i].i, out[i].j);
    }
    return 0;
}

第二次(全过)

思路

在第一次的前提下做优化吧,我这里主要想到了两种优化方案,一个是把算法变成在线的,但是通过这种方法还是超时了,另一个是加入二分把算法简化,这里写的是加入二分的版本
用一个数组chainSum储存链的有序和用于后面的二分查找,显然的,链中从i位置到j位置的价值和就是chainSum[j]-chainSum[i-1];

预期时间复杂度

nlogn

代码

CPP
//火星人用一串钻石链付款,钻石链切下来之后不能复原
//给你一串钻石链上面是他们的价值代码,和一个价格,你需要找出所有的支付方法,如果没有可能准确支付的时候找花费最小的方法。

//第一行两个数一个N<=10^5是钻石总数M<=10^8顾客需要支付的数额
//下一行给N个数字每个<=10^3是钻石的价值用空格分开,下标是1-N

//保证钻石的总价值对于支付是充足的,如果能准确支付,打印i-j角标,多个的时候按i升序.不能准确支付打印使超出最小的i-j也是按i升序.
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
typedef struct pairr
{
        int i;
        int j;
} pairr;
using namespace std;

int main(int argc, char const *argv[])
{
        //input
        int N, M;
        scanf("%d %d", &N, &M);
        int *chainSum = (int *)malloc(sizeof(int) * N + 5);
        int temporary;
        chainSum[0] = 0;
        for (int i = 1; i <= N; i++)
        {
                scanf("%d", &temporary);
                chainSum[i] = chainSum[i - 1] + temporary;
        }
        //用chainSum储存递增的链和用于二分查找.
        //process
        int mindifference = INT32_MAX;
        vector<pairr> result;
        for (int k = 1; k <= N; k++)
        {
                //二分查找
                int left = k, right = N;
                int mid;
                int j; //用于存放二分到的右端,左端固定是k.
                while (left < right)
                {
                        mid = (left + right)/2;
                        if (chainSum[mid] - chainSum[k - 1] >= M)
                        {
                                right = mid;
                        }
                        else
                        {
                                left = mid + 1;
                        }
                }
                j = right;
                while (chainSum[j] - chainSum[k] > M) //把前面可能多出的小价值除去.
                {
                        k++;
                }
                int paidValue = chainSum[j] - chainSum[k - 1]; //差额
                if (paidValue>=M)
                {
                        //差额比最小差小的时候清空result数组,加入当前组
                        //相等的时候加入当前组
                        //大的时候什么都不做
                        int difference=paidValue-M;
                        if (difference < mindifference)
                        {
                                mindifference = difference;
                                result.clear();
                                result.push_back({k, j});
                        }
                        else if (difference == mindifference)
                        {
                                result.push_back({k, j});
                        }
                }
        }
        //output
        for (int i = 0; i < result.size(); i++)
        {
                printf("%d-%d\n", result[i].i, result[i].j);
        }
        return 0;
}

运行用时

运行结果

看在我写了这么多注释的份上可以给我点个赞嘛,求求惹=]砰砰砰,给我加点写下去的油呀@.@

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值