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 (<=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.
Output Specification:
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.
Sample Input 1:16 15 3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13Sample Output 1:
1-5 4-6 7-8 11-11Sample Input 2:
5 13 2 4 5 7 9Sample Output 2:
2-4 4-5
题目要求一般化后,可以描述为:在所有元素为正的线性表中找出所有满足题目要求的子列和的子列的位置,如果没有满足要求的子列,则找出所有跟题目要求的子列和最接近的子列的位置。
为了解决这个问题,我们设置两个指针表示当前处理的子列位置,分别为start和end,为了符合题目要求,我们设线性表从1-N标号,线性表存储在容器N中,定义一个sum来记录当前子列的和,使用两个容器answers和minCurs分别记录满足题目要求的子列的位置(start和end)与跟题目要求最接近的所有子列位置。
①初始化,start=end=1,answers和minCurs清空,minSum=INF用于处理跟题目接近的子列,M存着题目要求的子列和。
②定义一个last_end=-1,在子列向前推进时,如果sum>M,说明start应该后移,而end不动,这时候为了防止end位置被重复累加,判断last_end是否等于end,不等于才累加,保证同一个end只被处理一次。
③核心逻辑如下:
1. 判断last_end是否等于end,不等于则让last_end=end; sum+=N[end];
2. 对当前sum进行判断
2.1 如果sum < M,说明子列和偏小,应该继续向前推进,即end++;
2.2 如果sum == M,说明找到了合适的子列,记录当前的start和end到answers容器,接下来应该让start向前,寻找新的子列,处理方法为sum-=N[start]; start++; 因为start在原来的位置正好是M,所以现在肯定小于M,子列应该继续推进,即end++;
2.3 如果sum > M,说明子列和偏大,首先判断当前子列和sum与已经记录的minSum哪个大
2.3.1 如果sum < minSum,说明找到了更小的接近题目要求的子列,这时候应该清空以前minCurs,然后记录当前位置,并且更新minSum为sum。
2.3.2 如果sum == minSum,说明发现了新的最接近条件的子列,继续压入minCurs即可。
除此之外,这时候因为sum偏大, 子列不应当向前推进,因此end不变,只让start前进,从而缩小子列的和,前面的last_end可以保证start活动时end不被累加。
3. 处理结束之后,先判断answers是否有值,有则输出,然后直接程序结束。
4. 如果answers没有值,则输出所有minCurs中的元素。
题目的注意事项:使用cin和cout有一个测试点会超时,在平时的练习中尽量使用scanf和printf。
程序如下:
#include <iostream> #include <vector> #include <stdio.h> #include <stdlib.h> using namespace std; struct Cur{ int start; int end; Cur(int s, int e) : start(s), end(e) {} }; int main() { int N; long M; long minSum = 900000000; int start,end; vector<Cur> answers; vector<Cur> minCurs; answers.clear(); minCurs.clear(); int value; cin >> N >> M; vector<int> chain(N + 1); for(int i = 1; i <= N; i++){ scanf("%d",&value); chain[i] = value; } start = end = 1; int sum = 0; int last_end = -1; while(end <= N){ if(end != last_end){ last_end = end; sum += chain[end]; } if(sum < M){ end++; }else if(sum == M){ answers.push_back(Cur(start,end)); sum -= chain[start]; start++; end++; }else{ // sum > M if(minSum > sum){ minSum = sum; minCurs.clear(); minCurs.push_back(Cur(start,end)); }else if(minSum == sum){ minCurs.push_back(Cur(start,end)); } sum -= chain[start]; start++; } } for(int i = 0; i < answers.size(); i++){ Cur c = answers[i]; printf("%d-%d\n",c.start,c.end); } if(answers.size() > 0) return 0; for(int i = 0; i < minCurs.size(); i++){ Cur c = minCurs[i]; printf("%d-%d\n",c.start,c.end); } return 0; }