1. 程式人生 > >PAT 1044 Shopping in Mars[二分][難]

PAT 1044 Shopping in Mars[二分][難]

ssi emp sample ffi 分享 pos ack any options

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-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 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

題目大意:給出一串數表示鉆石的價值,並且給出m,如果有一連串鉆石價值正好等於m,那麽就輸出這一串;如果沒有正好相等的和,那麽就輸出那些>m,並且和m差值最小的!

//第一次提交這樣,就是通過兩層循環來做,

技術分享圖片
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int dio[100001];
int main() {
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++){
        cin>>dio[i];
    }
    bool flag=false;
    for(int i=0;i<n;i++){
        int sum=0;
        for(int j=i;j<n;j++){
            sum+=dio[j];
            if(sum==m){
                flag=true;
                cout<<i+1<<-<<j+1<<\n;break;
            }
        }
    }
    vector<int> vt;
    if(!flag){//如果沒有解。
        int mini=100001;
        for(int i=0;i<n;i++){
            int sum=0;
            for(int j=i;j<n;j++){
                sum+=dio[j];
                if(sum>m&&sum-m<mini){
                    vt.clear();
                    vt.push_back(i+1);
                    vt.push_back(j+1);
                    //cout<<i+1<<"="<<j+1<<" "<<sum<<‘\n‘;
                    mini=sum-m;break;
                }else if(sum>m&&sum-m==mini){
                    vt.push_back(i+1);
                    vt.push_back(j+1);
                   // cout<<i+1<<"+"<<j+1<<‘\n‘;
                    break;
                }
            }
        }
    }
    for(int i=0;i<vt.size();i+=2){
        cout<<vt[i]<<-<<vt[i+1]<<\n;
    }
    return 0;
}
View Code

//但是提交到牛客網上,

運行超時:您的程序未能在規定時間內運行結束,請檢查是否循環有錯或算法復雜度過大。
case通過率為40.00%

//頓時感覺涼涼。提交到pat上,2,3,5測試點運行超時。

代碼來自:https://www.liuchuo.net/archives/2939

#include <iostream>
#include <vector>
using namespace std;
vector<int> sum, resultArr;
int n, m;
void Func(int i, int &j, int &tempsum) {//這個求的是從i到j的和。
    //表面上是left,但是while裏的if條件仍然只是>=m
    int left = i, right = n;
    while(left < right) {
        int mid = (left + right) / 2;
        if(sum[mid] - sum[i-1] >= m)
            right = mid;//必須是=mid,如果是mid-1,那很有可能和就不夠了。
        else
            left = mid + 1;
    }
    j = right;
    tempsum = sum[j] - sum[i-1];
}
int main() {
    scanf("%d%d", &n, &m);
    sum.resize(n+1);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &sum[i]);
        sum[i] += sum[i-1];//這是前綴和?
    }
    int minans = sum[n];
    for(int i = 1; i <= n; i++) {
        int j, tempsum;
        Func(i, j, tempsum);//直接傳引用。
        if(tempsum > minans) continue;//感覺這一句不用吧,肯定不會大於的。
        //明白了,這個在i=1第一次循環的時候是沒用,但是minans更新之後就有用了。
        //判斷>當前的都不選擇。
        if(tempsum >= m) {
            if(tempsum < minans) {
                resultArr.clear();
                minans = tempsum;
            }
            resultArr.push_back(i);
            resultArr.push_back(j);
        }
    }
    for(int i = 0; i < resultArr.size(); i += 2)
        printf("%d-%d\n", resultArr[i], resultArr[i+1]);
    return 0;
}

//學習了

1.當程序正常超時時,需要考慮二分法。

2.使用引用傳值,減少函數調用時間。

3.這裏的mid求的不是到left的和,而是到i的和!和往常的不一樣。

//這個二分我感覺確實挺難的,相不到,

代碼來自:https://www.cnblogs.com/chenxiwenruo/p/6677802.html

技術分享圖片
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <vector>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=100000+5;
int n,m;
int diamond[maxn];
struct Ans{
    int i,j;
};

vector<Ans> ans;
int main()
{
    scanf("%d %d\n",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&diamond[i]);
    int minlost=INF;
    Ans tmp;
    int sum=0;
    int start=1;
    for(int i=1;i<=n;i++){
        sum+=diamond[i];
    //printf("%d %d %d %d %d\n",start,i,sum,sum-m,minlost);
        if(sum<m)
            continue;
        if(sum-m<=minlost){
            if(sum-m<minlost){
                ans.clear();
                minlost=sum-m;
            }
            tmp.i=start;
            tmp.j=i;
            ans.push_back(tmp);
        }
        if(sum>=m && start<i){//這裏是在做什麽?
            sum-=diamond[i];//明白了,-鉆石i的值,和鉆石start的值,
            i--;//如果減去diamond[i],並且減去start的值,那麽總和肯定小於m,那麽
            sum-=diamond[start];//肯定要就著i往下加了。
            start++;//目前的sum肯定達不到m,所以肯定需要往後加。
            printf("%d\n",sum);
        }
    }
    for(int i=0;i<ans.size();i++){
        printf("%d-%d\n",ans[i].i,ans[i].j);
    }
    return 0;
}
View Code

//這個也寫得很好,但是我有點不太理解。

// 但是接著說上述二分方法的話,也就是原來二分,求和之後二分的方法。

PAT 1044 Shopping in Mars[二分][難]