• #417 Div2 Problem C Sagheer and Nubian Market (二分 && std::accumulate)


    题目链接 : http://codeforces.com/problemset/problem/812/C

    题意 : 给你 n 件物品和你拥有的钱 S, 接下来给出这 n 件物品的价格, 这些物品的价值不是固定不变的, 价格的变化公式是 a[i]+k*i (i代表第 i 件物品, k 代表你选择买的物品数量, a[i]为物品的底价), 现问你最多能够买多少件物品和所买物品总和, 输出时应该使得所买物品总和尽量小

    分析 : 如果我当前能买 k 件物品, 那我肯定能买数量小于 k 的物品, 如果我当前买不起 k 件物品, 那我肯定也不能买比 k 件要多的物品。所以可以考虑二分解法, 在1~n之间二分查找 k, 这里需要注意的是, 在二分的过程中应该需要对当前的价格进行更新和排序, 才能保证最后输出的物品总和尽量小!

    技巧 : 这里有需要计算数组前 k 个的和, 可以考虑使用 std::accumulate(begin, end, base), 代表从数组的arr[begin]加到arr[end]的和再加上base, 也就是在base的基础上求arr数组的begin~end的和, 这里有详细介绍(值得一提的是复杂度是 O(n) ) http://classfoo.com/ccby/article/Y749fK

    std::accumulate函数应用举例 : 

    #include <vector>
    #include <numeric>
    #include <functional>
    #include <iostream>
    
    using namespace std;
    
    int main( ) 
    {
    
       vector <int> v1, v2( 20 );
       vector <int>::iterator Iter1, Iter2;
    
       int i;
       for ( i = 1 ; i < 21 ; i++ )
       {
          v1.push_back( i );
       }
    
       cout << "最初向量v1中个元素的值为:
     ( " ;
       for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ )
          cout << *Iter1 << " ";
       cout << ")." << endl;
    
       // accumulate函数的第一个功能,求和
       int total;
       total = accumulate ( v1.begin ( ) , v1.end ( ) , 0 );
    
       cout << "整数从1到20的和为: " 
            << total << "." << endl;
    
       // 构造一个前n项和的向量
       int j = 0, partotal;
       for ( Iter1 = v1.begin( ) + 1; Iter1 != v1.end( ) + 1 ; Iter1++ )
       {
          partotal = accumulate ( v1.begin ( ) , Iter1 , 0 );
          v2 [ j ] = partotal;
          j++;
       }
    
       cout << "前n项和分别为:
     ( " ;
       for ( Iter2 = v2.begin( ) ; Iter2 != v2.end( ) ; Iter2++ )
          cout << *Iter2 << " ";
       cout << ")." << endl << endl;
    
       // accumulate函数的第二个功能,计算连乘积
       vector <int> v3, v4( 10 );
       vector <int>::iterator Iter3, Iter4;
    
       int s;
       for ( s = 1 ; s < 11 ; s++ )
       {
          v3.push_back( s );
       }
    
       cout << "向量v3的初始值分别为:
     ( " ;
       for ( Iter3 = v3.begin( ) ; Iter3 != v3.end( ) ; Iter3++ )
          cout << *Iter3 << " ";
       cout << ")." << endl;
    
       int ptotal;
       ptotal = accumulate ( v3.begin ( ) , v3.end ( ) , 1 , multiplies<int>( ) );
    
       cout << "整数1到10的连乘积为: " 
            << ptotal << "." << endl;
    
       // 构造一个前n项积的向量
       int k = 0, ppartotal;
       for ( Iter3 = v3.begin( ) + 1; Iter3 != v3.end( ) + 1 ; Iter3++ ) {
          ppartotal = accumulate ( v3.begin ( ) , Iter3 , 1 , multiplies<int>( ) );
          v4 [ k ] = ppartotal;
          k++;
       }
    
       cout << "前n项积分别为:
     ( " ;
       for ( Iter4 = v4.begin( ) ; Iter4 != v4.end( ) ; Iter4++ )
          cout << *Iter4 << " ";
       cout << ")." << endl;
    }
    View Code

    瞎想 : 一开始是在想是否能用背包做, 事实证明在S那个数据量下是不可能的, 而且背包的话, 这里要求的是总和尽量小。

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int maxn = 1e5+10;
    LL arr[maxn], change[maxn];
    LL n, s;
    LL cal(LL k)
    {
        for(int i=1; i<=n; i++) change[i] = i*k+arr[i];
        sort(change+1, change+1+n);
        LL ans = accumulate(change+1, change+1+k, 0LL);
        return ans;
    }
    int main(void)
    {
        scanf("%lld %lld", &n, &s);
        for(int i=1; i<=n; i++)
            scanf("%lld", &arr[i]);
        LL L = 1, R = n, mid;
        while(L<=R){
            mid = L + ((R-L)>>1);
            LL sum = cal(mid);
            if(sum <= s) L = mid + 1;
            else R = mid - 1;
        }
        if(L==1){
            puts("0 0");
            return 0;
        }
        L--;
        printf("%lld %lld
    ", L, cal(L));
        return 0;
    }
    View Code
  • 相关阅读:
    老陌与博客
    有关模式窗体和无(非)模式窗体的区别
    10月9日至10月22日备忘录
    9月4日至9月10日备忘录
    VS2015 远程调试:Remote Debugger
    8月28日至9月3日备忘录
    8月21日至8月27日技术积累
    用函数方法实现迭代器
    python中eval, exec, execfile,和compile(转载)
    dev 中的GridControl中的行实现选择的功能实现
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/6938958.html
Copyright © 2020-2023  润新知