stl 基础数据结构乱搞 (持续更新)

2018-8-25

ccpc-2018网络赛 A   buy and resell   hdu-6438   http://acm.hdu.edu.cn/showproblem.php?pid=6438 贪心,伪持续化  priority_queue   pair

题目大意:有n天,每天都可以进行买入(花费ai),卖出(收益ai-ak),什么都不干三种操作,问可以获得的最大收益和获得最大收益之下的最小交易次数(什么也不做不算交易次数)

初始思路:输入离散的点,贪心,寻找每一个最长上升段,用末尾元素-起始元素作为贡献,直到没有上升段为止。

如果是朴素算法面对一个纯上升序列的数据复杂度O(n^2)gg

算法优化:在线处理,考虑当前状态的最优解,由于每次求上升段用的是起始元素和末尾元素,考虑将数据用上升优先队列来存储,每次输入新的元素,如果队列不为空的话,用其和最高优先度(最小)的元素作比较

贪心,最优的方案一定是买和卖次数相同,我们每次考虑当前点的卖出,然后往前找最优的买入点,同时,最优的方案可能有多个,但是最优的结果只有一个(1,2,3,4这样的数据用1-4,2-3配对和1-3,2-4这样配对产生的收益相同)

如果该元素小于等于最高优先度的元素,那么在这个点卖出不会对最大收益产生任何贡献,将其加入队列

如果大于最高优先度的元素

这个时候关键点来了,当前最高收益不一定是整个结果的最优方案,比如 2 5 7这样的数据,所以想得到最优的方案,必须要对数据的存储进行处理,达到可以反悔的效果,从而得到最优方案

将这个队列的存储元素改为pair fir的值为val,一旦新输入的元素和队首元素配对,pop出当前队首元素  ans+=now-q.top;然后push两次当前元素进队列,分别为{val,1},{val,2} 一旦这个元素成为队首,这样对首就有两个一样的fir值的pair,当前的贪心最优方案一定是和最小的元素配对,如果pair的sec值为1,表示可以反悔,也就是说 贡献改为 当前元素 和 和当前对首配对的老的队首(2333) ,交易次数不变 , 并且当前的对首还存在于队列,ans+=now-q.top();相当于找了一个中间点两次贡献等于这次操作的总贡献

回过头来想,如果没配对成功的话,只push一次,pair的sec值设为2,表示不能反悔。

如果遇到当前对首元素sec值为2,此时的贪心策略仍旧是选择队首元素进行新的一次买卖配对,而不是选择老对首更小的 。

总复杂度为O(nlogn),完美契合总数据 5e5 

#include<iostream>
#include<queue>
#include<cstring>
#define ll long long
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        priority_queue < pair<ll,ll>,vector<pair<ll,ll> > ,greater< pair<ll,ll> > > q;
        int n,time=0;
        ll ans=0;
        ll temp;
        cin>>n;
        while(n--)
        {
            cin>>temp;
            if(!q.empty()&&temp>q.top().first)
            {
                if(q.top().second==1)//反悔
                {
                    time--;
                }
                else//反悔的话 time不变,不反悔的话time+=2
                {
                    time++;
                }
                ans+=temp-q.top().first;//当前最大贡献
                time++;
                q.pop();
                q.push({temp,1});q.push({temp,2});
            }
            else// 队列为空或者该元素是当前队列中的最小元素
            {
                q.push({temp,2});
            }
        }
        cout<<ans<<' '<<time<<endl;
    }

    return 0;
}

codeforces #506_div3-C   http://codeforces.com/contest/1029/problem/C 模拟  multiset

题目大意:n个一维线段,给出每个线段的左右端点,问去掉一个线段之后,剩余n-1个线段的最大重合段[l,r] (这n-1个线段都有子集在[l,r]中)

思路:模拟,需要用到删除操作,并且同一个值可能有多个可能会影响结果的数据,开两个multiset存

开一个数组存放每组l,r循环遍历数组,每次删除这个线段, 当前最大重合段就是

min(right)-max(left),最后再把这组数据弄回去

#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#define ll long long
using namespace std;
multiset<int>l;
multiset<int>r;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int n;
    cin>>n;
    int L[300005];
    int R[300005];
    for(int i=1;i<=n;i++)
    {
        cin>>L[i]>>R[i];
        l.insert(L[i]);
        r.insert(R[i]);
    }
    multiset<int>::iterator iter;
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ll temp;
        l.erase(l.find(L[i]));
        r.erase(r.find(R[i]));
        iter=l.end();iter--;
        temp=*r.begin()-*iter;
        ans=max(temp,ans);
        l.insert(L[i]);
        r.insert(R[i]);
    }
    cout<<ans<<endl;







    return 0;
}

codeforces #506_div3-Dhttp://codeforces.com/contest/1029/problem/D 思路转换  模拟    map数组 计数map 

题目大意:给定n个数和 k,n个数字任意组合 (123 5变为1235或5123)问有多少种组合满足组合后的数%p==0

两个数字拼凑可以理解成前一个数*后一个数的位数+后一个数

数字最大不超过10^9也就是数不超过9位

暴力,先统计每个数字的位数,把每个数字 *(1 10 100 ... 1e9)在mod k 的数字保存下来,转换为两个数的加法==k   

而n只有2e5  开10个map(写的时候map能开数组也是惊了) m[i][j]记录前面*i个10之后在mod k之下 结果为j的数量

 复杂度 O(nlogn) 再转化为遍历每个数字的不同num,在map里找有没有对应的k-num 注意这里是加法,不是乘法

#include<iostream>
#include<map>
using namespace std;
typedef long long LL;
const int maxn=200005;
map<int,int> mp[11];
int a[maxn];
int w[maxn];
int n,k,x,c;
int f[11];
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];//存储原始输入的数
        x=a[i];
        c=0;
        while(x)
        {
            x/=10;
            c++;
        }
        w[i]=c;
        mp[c][a[i]%k]++;//mp[i][j]表示这个数字有i位,在 mod k 下余几
    }
    f[0]=1;
    for(int i=1;i<=10;i++)
    {
        f[i]=10LL*f[i-1]%k;
    }
    LL ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=10;j++)
        {
            x=(k-1LL*a[i]*f[j]%k)%k;
            if(mp[j].find(x)!=mp[j].end())
            {
                ans+=mp[j][x];
                if(a[i]%k==x&&w[i]==j)
                {
                    ans--;
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neuq_zsmj/article/details/82077735