首页 > 试题广场 >

Shopping in Mars (25)

[编程题]Shopping in Mars (25)
  • 热度指数:2182 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
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.

输入描述:
Each input file contains one test case.  For each case, the first line contains 2 numbers: N (<=105), the total number of diamonds on the chain, and M (<=108), the amount that the customer has to pay.  Then the next line contains N positive numbers D1 ... DN (Di<=103 for all i=1, ..., N) which are the values of the diamonds.  All the numbers in a line are separated by a space.


输出描述:
For each test case, print "i-j" in a line for each pair of i <= j such 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-j" for pairs of i <= j such 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.
示例1

输入

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

输出

1-5
4-6
7-8
11-11
/*
1.因为是求区间和 可以采用前缀和的思想 
s[i]=s[i-1]+a[i]
这样在求[l,r]之间的值的时候
s[r]-s[l-1]即可
2.由于对于s[n]这个数组是满足单调性的
故可以用双指针算法
设置l=0,r=0
if(s[r]-s[l-1]>tar)l++;//i向右走 这个区间和就会变小
else if(s[r]-s[l-1]<tar)r++;太小了 r向右走
else

    printf("%d-%d\n",l,r);
    l++;
    r++;
*/
#include <iostream>
(720)#include <algorithm>
#include <cstdio>
(802)#include <limits.h>
using namespace std;
const int N =100010;
int a[N],s[N];
int main()
{
    int n,tar;
    cin>>n>>tar;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        s[i]+=s[i-1]+a[i];
    }
    int l=1,r=1;
    while(l<=n&&r<=n)
    {
        if(s[r]-s[l-1]>tar)l++;
        else if(s[r]-s[l-1]<tar)r++;
        else
        {
            printf("%d-%d\n",l,r);
            l++;
            r++;
        }
    }
    return 0;
}

发表于 2020-03-06 11:26:52 回复(1)
牛客网上面时间是1s,pat是300ms,暴力做法牛客网可以通过但是pat会存在超时的情况
可以用动态规划的做法:
(1)用dp[i] 表示以values[i]结尾的大于等于m的最小值。用数组start表示每个序列和的开始下标。
(2)dp[i] = dp[i-1]+values[i],如果dp[i]>= m,就需要判断减去序列开始的值,如果还是大于,则继续循环,知道不能再减位置,保证每个dp[i]是以values[i]结尾且大于等于m的最小值。
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;
const int maxn = 100001;
const int INF = 0x7fffffff;
int values[maxn];
int dp[maxn];
int start[maxn];  /**存放每个序列和的开始下标*/
int main() {
    int n,m;
    scanf("%d %d", &n, &m);
    for(int i=1; i<=n; i++) {
        scanf("%d", values+i);
    }
    fill(start, start+maxn, -1);
    fill(dp, dp+maxn, 0);
    dp[1] = values[1];
    start[1] = 1;

    bool flag = false;  /**设置一个标志位表明是否存在刚好等于m的序列和*/
    int min_value = INF;   /**记录大于等于m的最小序列和*/
    min_value = dp[1]>=m ? dp[1]:INF;
    for(int i=2; i<=n; i++) {
        dp[i] = dp[i-1]+values[i];
        start[i] = start[i-1];
        if(dp[i] == m) {
            flag = true;
        } else if(dp[i]>m) {
            int j = start[i-1];
            while(j<i) {
                if(dp[i]-values[j]>=m) {
                    dp[i] -= values[j];
                    j++;
                } else {
                    break;
                }
            }
            start[i] = j;
            min_value = min(min_value, dp[i]);
        }
    }

    if(!flag) {
        m = min_value;
    }

    for(int i=1; i<=n; i++) {
        if(dp[i] == m) {
            printf("%d-%d\n", start[i], i);
        }
    }
}


发表于 2019-09-01 15:50:52 回复(0)
#include <iostream>
#include <algorithm>
using namespace std;
int s[100010];
int main(){
    int x,y,neary=100000010;
    cin>>x>>y;
    s[0]=0;
    for(int i=1;i<=x;i++){
        cin>>s[i];
        s[i]+=s[i-1];
    }
    for(int i=1;i<=x;i++){  //枚举左端点
        int j=lower_bound(s+i,s+x+1,s[i-1]+y)-s;      //求右端点
        if(s[j]-s[i-1]==y)
            neary=y;
        else if(s[j]-s[i-1]>=y && s[j]-s[i-1]<neary)  //有大于y的解并小于neary
            neary=s[j]-s[i-1];
    }
    for(int i=1;i<=x;i++){
        int j=lower_bound(s+i,s+x+1,s[i-1]+neary)-s;  //找出所有等于neary的区间
        if(s[j]-s[i-1]==neary)
            cout<<i<<"-"<<j<<'\n';
    }
    return 0;
}

发表于 2018-02-03 22:52:52 回复(0)
上面两个在pat上都会超时,用前后指针遍历,O(N),O(1)可解
发表于 2016-01-09 14:15:59 回复(0)

思路

利用前缀和数组和双指针找符合条件的值并成对按i的大小存入vector容器,最后讲容器中的值成对输出即可,pat测试最长时间44ms.

代码(c++)

#include <cstdio>
(802)#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 100010;
const int INF = 0x3fffffff;

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    int v[maxn], sum[maxn]; //sum存储前缀和
    v[0] = 0, sum[0] = 0;
    int temp; //临时变量
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &temp);
        v[i] = temp;
        sum[i] = sum[i - 1] + v[i];
    }
    int minCost = INF; //存储找到的最小值
    vector<int> ans;   //结果容器
    int i = 0, j = 0;
    while (i <= (n - 1) && j <= n)
    {
        if ((sum[j] - sum[i]) < m)
        {
            j++;
            continue;
        }
        if (sum[j] - sum[i] < minCost)
        {
            minCost = sum[j] - sum[i];
            ans.clear(); //找到更接近的值,清空容器重新计数
            ans.push_back(i + 1);
            ans.push_back(j);
            i++;
            continue;
        }
        else if (sum[j] - sum[i] == minCost)
        {
            ans.push_back(i + 1); //相同值添加进结果
            ans.push_back(j);
            i++;
            continue;
        }
        i++;
    }
    for (int i = 0; i + 1 < ans.size(); i += 2)
    {
        printf("%d-%d\n", ans[i], ans[i + 1]);
    }
    return 0;
}
发表于 2020-03-23 19:26:41 回复(0)
/*滑动窗口思想*/
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
const int AX = 1e5 + 66 ;
int a[AX] ; 
vector<P>res;
int main(){
	int n , m ; 
	scanf("%d%d",&n,&m);
	for( int i = 1 ; i <= n ; i++ ){
		scanf("%d",&a[i]);
	}
	int st = 1 , ed = 1 ;
	int dv = INF ; 
	int sum = 0 ; 
	while( st <= ed ){
		while( ed <= n && sum < m ) sum += a[ed++] ;  
		if( sum < m ) break ; 
		if( dv > sum - m ){
			dv = sum - m ; 
			res.clear() ; 
			res.push_back(P(st,ed-1));
		}else if( dv == sum - m ){
			res.push_back(P(st,ed-1));
		}
		sum -= a[st++] ; 
	}
	for( auto x : res ){
		printf("%d-%d\n",x.first,x.second);
	}
	return 0 ; 
}

编辑于 2020-02-26 13:49:33 回复(0)
二分解法
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long LL;
LL sum[N],nearest = 1e3+10;//前缀和,最短差距
vector<pair<int,int> > nearV;
int binSearch(int l, int r, LL x){//在[l,r]找大于等于x的下标
    while(l<r){
        int mid = (l+r)>>1;
        if(sum[mid]>=x) r = mid;
        else l = mid + 1;
    }
    if(sum[l]<x) return l+1;//查找失败
    return l;
}
int main(){
    int n,res,x,flag = 0;
    scanf("%d%d",&n,&res);
    for(int i = 1; i <=n; i++){
        scanf("%d",&x);
        sum[i] = sum[i-1] + x;
    }
    for(int i = 1; i <= n; i++){
        //int j = lower_bound(sum+i,sum+n+1,sum[i-1]+res)-sum;
        int j = binSearch(i,n,sum[i-1]+res);
        if(j>n) continue;//查找失败
        LL gap = sum[j]-sum[i-1]-res;
        if(gap==0){
            flag = 1;
            printf("%d-%d\n",i,j);
        }
        else if(gap <= nearest){
            if(gap == nearest) nearV.push_back(make_pair(i,j));
            else {
                nearest = gap;
                nearV.clear(), nearV.push_back(make_pair(i,j));
            }
        }
    }
    if(!flag){
        for(auto p:nearV){
            printf("%d-%d\n",p.first,p.second);
        }
    }
    return 0;
}

编辑于 2019-05-05 21:26:27 回复(0)

题目大意:在一串数字A中找到和为指定和m的区间,如果没有找到,则输出距离指定和相差最小的区间。
采用Two Pointers方法,预处理这一串数字得到前n项和,设立两个指针i,j,如果Asum[i..j] == m,则找到目标,记下该区间,否则Asum[i..j] > m,则记下区间以备最后“没有找到和相等于m的情况时输出”,i自增,因为此时Asum[i+1..j]一定是小于m的,则j不用变,但是i自增后有可能大于j,不要忘记将其指向i

Code

#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100100;
const int INF = 0x3fffffff;
int n, m;
int acusum[maxn];
vector<pair<int, int> > pairs;
vector<pair<int, int> > bak;

int main() {
    fill(acusum, acusum + maxn, 0);
    scanf("%d %d", &n, &m);
    int temp, min = INF;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &temp);
        acusum[i] = acusum[i - 1] + temp;
    }
    int i = 1, j = 1;
    while (i <= j && i <= n && j <= n) {
        while (acusum[j] - acusum[i - 1] < m && j <= n) {
            j++;
        }
        if (acusum[j] - acusum[i - 1] == m) {
            pairs.push_back(make_pair(i, j));
        } else {
            if ((int)abs((double)acusum[j] - (double)acusum[i - 1]) < min) {
                min = (int)abs((double)acusum[j] - (double)acusum[i - 1]);
                bak.clear();
                bak.push_back(make_pair(i, j));
            } else if ((int)abs((double)acusum[j] - (double)acusum[i - 1]) == min) {
                bak.push_back(make_pair(i, j));
            }
        }
        i++;
        if (j < i) {
            j = i;
        }
    }
    if (pairs.size() != 0) {
        for (int i = 0; i < pairs.size(); i++) {
            printf("%d-%d\n", pairs[i].first, pairs[i].second);
        }
    } else {
        for (int i = 0; i < bak.size(); i++) {
            printf("%d-%d\n", bak[i].first, bak[i].second);
        }
    }
    return 0;
}
编辑于 2018-09-02 18:11:33 回复(0)
pat上没通过,这里通过了。作为一个马上考研的人,该喜该悲?

#include<stdio.h>

int list[100005];
int sum[100005];
int n;
long long m;

int main(){
    scanf("%d %d",&n,&m);
    int diff=999999,start,end;
    bool flag=false;
    for(int i=1;i<=n;i++)
        scanf("%d",&list[i]);
    for(int i=1;i<=n;i++){
        int j=i+1;
        long long tmp=list[i];
        while(tmp<m&&j<=n){
            tmp+=list[j];
            j++;
        }
        if(tmp==m){
            printf("%d-%d\n",i,j-1);
            flag=true;
            continue;
        }
        else if(tmp-m<diff){
            start=i;
            end=j-1;
        }
    }
        if(!flag)
            printf("%d-%d\n",start,end);
    return 0;
}

发表于 2018-03-07 20:31:52 回复(1)
#include <iostream>
#include <map>

using namespace std;

const int MAXN = 100003;
int dp[MAXN] = {0};
map<int, int> M;

int main()
{     int n,m;     cin>>n>>m;     for(int i=1;i<=n;i++)     {         cin>>dp[i];         dp[i] += dp[i-1];         M[dp[i]] = i;     }     for(int i=1;i<=n;i++)     {         if(dp[i]<m)             continue;         else if(dp[i]==m)             cout<<"1-"<<i<<endl;         else{             int t = dp[i] - m;             if(!M.count(t))                 continue;             else{                 int p = M[t];                 cout<<p+1<<"-"<<i<<endl;             }         }     }     return 0;
}

发表于 2018-03-12 07:38:20 回复(0)
#include<bits/stdc++.h>
using namespace std;

const int Max=1e5+10;
const int INF=INT_MAX;

int main(){
    ios::sync_with_stdio(0);
	int n,m,nears=INF;
	cin>>n>>m;
	int sum[Max];
	sum[0]=0;
	for(int i=1;i<=n;i++){
		cin>>sum[i];
		sum[i]+=sum[i-1];
	}
	for(int i=1;i<=n;i++){
		int j=upper_bound(sum+i,sum+n+1,sum[i-1]+m)-sum;
		if(sum[j-1]-sum[i-1]==m){
			nears=m;
			break;
		}else if(j<=n&&sum[j]-sum[i-1]<nears){
			nears=sum[j]-sum[i-1];
		}
	}
	for(int i=1;i<=n;i++){
		int j=upper_bound(sum+i,sum+n+1,sum[i-1]+nears)-sum;
		if(sum[j-1]-sum[i-1]==nears){
			cout<<i<<'-'<<j-1<<endl;
		}
	}
	return 0;
}

发表于 2022-11-12 20:56:36 回复(2)
二分法
#include<algorithm>
#include<iostream>
using namespace std;
int na[100009];
int nb[100009];
int main()
{
	int n, m,an=0;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> na[i];
		an += na[i];
		nb[i] = an;
	}
	int i;
	for (i = 1; i <= n; i++) {
		int loc = n;
		int ti = i, tj = n;
		while (ti<=tj)
		{
			/*cout << "i="<<i << "	ti=" << ti << "	tj=" << tj << endl;*/
			loc = (ti + tj) / 2;
			if (nb[loc] - nb[i-1] == m) {
				cout << i << "-" << loc << endl;
				break;
			}
			else if (nb[loc] - nb[i-1] < m) {
				ti = loc+1;
			}
			else if (nb[loc] - nb[i-1] > m) {
				tj = loc-1;
			}
		}
	}
	return 0;
}


发表于 2021-11-25 21:45:42 回复(0)

将所有子序列的和,构成一个矩阵

例:

5 13
2 4 5 7 9

image-20210325164047650

矩阵中的数据是有一定的规律的,既从左到右,从下到上递增,可以按照这些数据的规律,寻找答案

由于数据最大有100000个,使用二维矩阵是很不理想的,或许上三角矩阵可以,当然可以模拟这个过程

#include <cstdio>
#include <vector>
using namespace std;
vector<int> res, sum;
int main() {
    int n, m,temp;
    int k = 1;              // 最接近目标的 1-k 序列
    int min = 99999999;     // 最接近目标的最小序列和
    scanf("%d %d", &n, &m);
    sum.resize(n + 1);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &temp);
        sum[i] = sum[i - 1] + temp; // 保存 1-i 的序列和
        if (sum[i]<m){              // 寻找最接近目标的序列和
            k=i;
        }
    }
    /* sum[k]有两种情况,一种是小于目标值,一种是刚好等于目标值*/
    if (sum[k] < m) {
        k++;        // 小于的时候,不满足题目要求,需要一个比目标值大的数
    }
    /* 1-k 的序列和最接近目标值*/
    temp = min=sum[k];
    int i = 1, j = k;
    while (i <= n && j <= n) {
        if (temp < m) {     // 小于目标值,往右搜索
            temp = sum[++j] - sum[i - 1];
            continue;
        }
       /*有更合适的选择或者相同的选择*/
        if (temp <= min) {
            if (temp < min) {   // 有更合适的序列 更新
                min = temp;
                res.clear();
            }
            /*加入到结果集*/
            res.push_back(i);
            res.push_back(j);
        }
        temp = sum[j] - sum[i++];   // 下移
    }
    for (int t = 0; t < res.size(); t += 2) {
        printf("%d-%d\n", res[t], res[t + 1]);
    }
    return 0;
}
发表于 2021-03-25 17:11:22 回复(0)
滑动窗口应对超时 永远的神
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<bits/stdc++.h>

#define INF 2147483647
#define MIN (INF+1)
#define ll long long

using namespace std;

struct solution {
    int left, right;
    int cost;
};

bool cmp(solution s1, solution s2) {
    return s1.left < s2.left;
}

// N (≤10 5), the total number of diamonds on the chain
// M (≤10 8), the amount that the customer has to pay
int N, M;
vector<int> dias;
vector<solution> solutions;
int min_loss = INF;

int main() {

    scanf("%d %d", &N, &M);
    for (int i = 0; i < N; ++i) {
        int d;
        scanf("%d", &d);
        dias.push_back(d);
    }

    int left = 0, right = 0;
    int ol = 0, ori = 0;
    int sum = dias[0];
    while(left < N) {
        if(right >= N)
            break;
        if(left > ol)
            sum -= dias[ol];
        if(right > ori)
            sum += dias[right];
        // cout << sum << endl;
        ol = left;
        ori = right;
        // 滑动窗口永远的神!
        // int sum = accumulate(dias.begin()+left, dias.begin()+right + 1, 0);
        if(sum == M) {
            min_loss = 0;
            solution s{};
            s.left = left;
            s.right = right;
            s.cost = M;
            solutions.push_back(s);
            left++;
            right++;
        } else if (sum < M) {
            if(right == N - 1)
                break;
            right++;
        } else {
            if(sum - M <= min_loss) {
                min_loss = sum - M;
                solution s{};
                s.left = left;
                s.right = right;
                s.cost = sum;
                solutions.push_back(s);
            }
            left++;
        }
    }

    sort(solutions.begin(), solutions.end(), cmp);

    for (int j = 0; j < solutions.size(); ++j) {
        if(solutions[j].cost == min_loss + M) {
            printf("%d-%d\n", solutions[j].left + 1, solutions[j].right + 1);
        }
    }

    return 0;
}


发表于 2020-09-22 00:54:24 回复(0)
//开始想用dp,不知道咋用,参照了一下别人的思路
//dp[i]表示当前最接近M的连续序列和,用start数组存下标起始位置,
//dp[i+1]=dp[i]+values[i],if(dp[i+1]>M),则需要回退。
//当dp[i]<M时,dp[i]不变,当dp[i]>M时,需要减去从下标start开始的values[start],直到找到一个start使
//从它开始到i的这一串钻石价值是最近M的(>=M)
#include<iostream>
(720)#include<vector>
#include<algorithm>
(831)#include<limits.h>
using namespace std;
typedef pair<int, int>PP;
const int maxn = 100001;
int main() {
	int m, n;
	cin >> n >> m;
	int values[maxn] = { 0 };
	int start = 1;
	int dp[maxn] = { 0 };
	vector<PP>answer;
	int min = INT_MAX;
	for (int i = 1; i <= n; i++) {
		cin >> values[i];
		if (i == 1) {
			dp[i] = values[i];
		}
		else {
			dp[i] = dp[i - 1] + values[i];
		}
		if (dp[i] >= m) {
			while (dp[i] >= m && start <= i) {
				dp[i] = dp[i] - values[start];
				start++;
			}
			start--;
			dp[i] += values[start];
			if (dp[i] - m == min) {
				answer.push_back(PP(start, i));
			}
			else if (dp[i] - m < min) {
				answer.clear();
				min = dp[i] - m;
				answer.push_back(PP(start, i));
			}
		}
	}
	sort(answer.begin(), answer.end());
	for (int i = 0; i < answer.size(); i++) {
		printf("%d-%d\n", answer[i].first, answer[i].second);
	}
}

发表于 2020-03-24 13:10:45 回复(0)
这题用切片和总和函数很快能得出来
a,b = list(map(int,input().split(' '))),list(map(int,input().split(' ')))
i = 0;j = 1;m = 10 ** 10;n = [];f = 1

while j < a[0] + 1:
    if sum(b[i:j]) < a[1]:
        j += 1
    elif sum(b[i:j]) > a[1]:
        if f:
            if m > sum(b[i:j]):
                m = sum(b[i:j]);n.clear()
                n.append(str(i + 1) + '-' + str(j))
            elif m == sum(b[i:j]):
                n.append(str(i + 1) + '-' + str(j))
        i += 1
    else:
        print(str(i + 1) + '-' + str(j))
        f = 0;i += 1;j += 1

if f:
    for i in n:
        print(i)
但是显然会超时,只能不用python的函数,用变量表示
a,b = list(map(int,input().split(' '))),list(map(int,input().split(' ')))
i = 0;j = 1;m = 10 ** 10;n = [];f = 1;p = b[0]

while j < a[0]:
    if p < a[1]:
        p += b[j];j += 1
    elif p > a[1]:
        if f:
            if m > p:
                m = p;n.clear()
                n.append(str(i + 1) + '-' + str(j))
            elif m == p:
                n.append(str(i + 1) + '-' + str(j))
        p -= b[i];i += 1
    else:
        print(str(i + 1) + '-' + str(j))
        f = 0;p +=b[j] - b[i];i += 1;j += 1

while p > a[1]:
    if f:
        if m > p:
            m = p;n.clear()
            n.append(str(i + 1) + '-' + str(j))
        elif m == p:
            n.append(str(i + 1) + '-' + str(j))
    p -= b[i];i += 1
    
else:
    if p == a[1]:f = 0;print(str(i + 1) + '-' + str(j))

if f:
    for k in n:
        print(k)
在牛客上全过,但在pat可能全过也可能超时一个3测试点,莫名其妙

编辑于 2020-02-03 18:20:16 回复(0)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

//1044 Shopping in Mars
//为了应对运行超时,优化一下
//对i,如果end_pos在j点,起码v[i]+v[i+1]+...+v[j-1]<m,这样才会需要再加上v[j]。
//由于v[i]非负,所以v[i+1]+...+v[j-1]<m必然也成立,所以从i+1点出发,最起码要加到j点才有可能>=m。
//这样我们可以省略从i+1点到j点的多次加法运算,因为summ=v[i]+v[i+1]+...+v[j]在i点已经算过了。
//在i+1点,只需要令summ-=v[i],然后就可以作为基础值。之后再决定是否继续加下去。
//优化之后,pat.3本来350ms,现在151ms。

vector<int> end_pos;
vector<int> min_sum;

int main() {

    int n, m, num;
    int min_cost = 1e8;
    cin >> n >> m;

    vector<int> v;
    for (int i = 0; i < n; ++i) {
         cin >> num;
        v.push_back(num);
    }

    //更新end_pos和min_sum
    int ind = -1, summ = 0;
    for (int i = 0; i < n; ++i) {
        //利用上一个结点的计算结果
        if (i > 0) {
            summ -= v[i - 1];
            if (summ >= m) {
                end_pos.push_back(ind);
                min_sum.push_back(summ);
                if (summ < min_cost) { min_cost = summ; }
                continue; //直接去下个i
            }
        }

        for (int j = ind + 1; j < n; ++j) {
            ind++;//保证就算不走break,ind也能表示终点
            summ += v[ind];
            if (summ >= m) {
                ind = j;
                break;
            }
        }

        end_pos.push_back(ind);
        min_sum.push_back(summ);
        //一开始忘记加summ>=m的判断,遇到3 5 1,2,4这样的输入,min_cost=4<m=5,就出错了 
        if (summ < min_cost && summ >= m) { min_cost = summ; }
    }

    //输出
    if (min_cost == m) {
        //存在最佳方案,则输出全部最佳方案
        for (int i = 0; i < n; ++i) {
            if (min_sum[i] == m) {
                cout << i + 1 << "-" << end_pos[i] + 1 << endl;
            }
        }
    }
    else {
        //输出损失最小方案
        for (int i = 0; i < n; ++i) {
            if (min_sum[i] == min_cost) {
                cout << i + 1 << "-" << end_pos[i] + 1 << endl;
            }
        }
    }

    return 0;
}



/*
//原始方案,pat.2,pat.5运行超时,过不去。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

//1044 Shopping in Mars
//这题钻石的编号是1~n,输出时要注意
//记录以给定位置为起点,所能达到的第一个>=m的位置,及其最小和。
//这题居然是个chain,不是环,亏我一开始还当环来写。。。


vector<int> end_pos;
vector<int> min_sum;

int main(){

    int n,m,num;
    int min_cost=1e8;
    cin>>n>>m;

    vector<int> v;
    for(int i=0;i<n;++i){
        cin>>num;
        v.push_back(num);
    }

    //更新end_pos和min_sum
    for(int i=0;i<n;++i){
        int ind,summ=0;
        for(int j=0;i+j<n;++j){
            ind = i+j;
            summ+=v[ind];
            if(summ>=m){
                break;
            }
        }
        end_pos.push_back(ind);
        min_sum.push_back(summ);
        //一开始忘记加summ>=m的判断,遇到3 5 1,2,4这样的输入,min_cost=4<m=5,就出错了
        if(summ<min_cost && summ>=m){min_cost=summ;}
    }

    //输出
    if(min_cost==m){
        //存在最佳方案,则输出全部最佳方案
        for(int i=0;i<n;++i){
            if(min_sum[i]==m){
                cout<<i+1<<"-"<<end_pos[i]+1<<endl;
            }
        }
    }
    else{
        //输出损失最小方案
        for(int i=0;i<n;++i){
            if(min_sum[i]==min_cost){
                cout<<i+1<<"-"<<end_pos[i]+1<<endl;
            }
        }
    }

    return 0;
}
*/
发表于 2019-08-06 16:51:18 回复(0)
建议不要在牛客网上刷真题,测试用例十分菜
发表于 2019-08-05 16:58:35 回复(0)
在PAT上用例2和用例5超时,其他正确,在这全正确
直接遍历,在另一个数组里记录从a[i]出发第一个超过pay值的部分和,然后再遍历输出就行了,疯狂遍历但并没有超时

#include<stdio.h>
#include<stdlib.h>
int main()
{
long int i=0,j=0,k=0,n=0,pay=0,num=0,min=0,x=0;
scanf("%ld",&n);
if(n<0||n>100000) exit(0);
long int a[n],b[3*n];
scanf("%ld",&pay);
if(pay<0||pay>100000000) exit(0);
for(i=0;i<n;i++)
{
scanf("%ld",&a[i]);
if(a[i]<0)exit(0);            /*到这完成所有输入*/
}
for(j=0;j<n;num=0,j++,k=j)
{
for(num=a[j];num<pay;k++,num=num+a[k]);
b[x]=num-pay,b[x+1]=j+1,b[x+2]=k+1;
x=x+3;
}/*在b数组中记录从a[i]开始第一个超过pay值的部分和以及对应的序号*/
for(i=0,min=b[0];i<3*n;i=i+3)
if(min>b[i])
min=b[i];
for(i=0;i<3*n;i=i+3)
if(b[i]==min)
printf("%ld-%ld\n",b[i+1],b[i+2]);/*第一次遍历输出值为0的项,也就是价值正好对应,如果没有再尝试输出值为1的...懒得优化了*/
}
编辑于 2019-07-19 13:13:12 回复(0)
不知道跟我一样用map做的多不多

My Code

#include<iostream>
#include<stdio.h>
#include<map>
using namespace std;
const int maxn = 100009;
map<int, int>simap;
int  input[maxn];
int main(){
    int n, sum,tsum=0;
    //get input data
    scanf("%d%d", &n, &sum);
    for (int i = 1; i <= n; i++){
        scanf("%d", &input[i]);
        tsum += input[i];
        input[i] = tsum;
        simap[tsum] = i;
    }
    //print the answer
    for (int i = 1; i <= n; i++){
        if (input[i] == sum){
            cout << 1 << "-"<<i << endl;
            continue;
        }
        int last = simap[input[i] - sum];
        if ( last == 0) continue;
        cout << last+1 << "-"<< i << endl;
    }
    return 0;
}

发表于 2019-04-16 22:57:12 回复(0)