2018 Spring Training STL

这篇博客主要介绍了在2018年春季训练中遇到的一些编程题目,涉及到使用STL和算法解决各种问题。其中包括使用stack模拟、卡特兰数的递推与高精度计算、并查集和队列的应用、智商检测题以及使用bitset和多集合优化的Floyd算法。作者还分享了在解决不等关系问题时的思考过程,以及如何结合set和并查集的数据结构来解决问题。
摘要由CSDN通过智能技术生成

https://cn.vjudge.net/contest/234044#overview
总体而言一部分傻逼题一部分难题
A.
傻逼题1
拿stack模拟一下即可。

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
int main()
{
    int n,i,j,k;
    string str1,str2;
    while(cin>>n){
        cin>>str1>>str2;reverse(str2.begin(),str2.end());
        vector<int>ans;
        stack<int>sta1,sta2;
        for(i=0;i<str2.size();i++)sta2.push(str2[i]);
        for(i=0;i<str1.size();i++){
            ans.push_back(1);sta1.push(str1[i]);
            while(!sta1.empty()&&sta1.top()==sta2.top())
                sta1.pop(),sta2.pop(),ans.push_back(2);
        }
        if(!sta1.empty()){
            puts("No.");
        }
        else{
            puts("Yes.");
            for(i=0;i<ans.size();i++)
                if(ans[i]==1)
                    puts("in");
                else puts("out");
        }
        puts("FINISH");
    }

    return 0;
}

B.HDU1023
裸的卡特兰数,不知道的话这题可以弃疗了(比如像本蒟蒻,摔
这里写图片描述
这里写图片描述
要么打表,网上有卡特兰数的表,要么递推求,int128是水不过去的,老老实实写高精吧,或者赶紧学一波java(摔

#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
using namespace std;

const int maxn = 1000;

struct bign{
    int d[maxn], len;

    void clean() { while(len > 1 && !d[len-1]) len--; }

    bign()          { memset(d, 0, sizeof(d)); len = 1; }
    bign(int num)   { *this = num; }
    bign(char* num) { *this = num; }
    bign operator = (const char* num){
        memset(d, 0, sizeof(d)); len = strlen(num);
        for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0';
        clean();
        return *this;
    }
    bign operator = (int num){
        char s[20]; sprintf(s, "%d", num);
        *this = s;
        return *this;
    }

    bign operator + (const bign& b){
        bign c = *this; int i;
        for (i = 0; i < b.len; i++){
            c.d[i] += b.d[i];
            if (c.d[i] > 9) c.d[i]%=10, c.d[i+1]++;
        }
        while (c.d[i] > 9) c.d[i++]%=10, c.d[i]++;
        c.len = max(len, b.len);
        if (c.d[i] && c.len <= i) c.len = i+1;
        return c;
    }
    bign operator - (const bign& b){
        bign c = *this; int i;
        for (i = 0; i < b.len; i++){
            c.d[i] -= b.d[i];
            if (c.d[i] < 0) c.d[i]+=10, c.d[i+1]--;
        }
        while (c.d[i] < 0) c.d[i++]+=10, c.d[i]--;
        c.clean();
        return c;
    }
    bign operator * (const bign& b)const{
        int i, j; bign c; c.len = len + b.len;
        for(j = 0; j < b.len; j++) for(i = 0; i < len; i++)
                c.d[i+j] += d[i] * b.d[j];
        for(i = 0; i < c.len-1; i++)
            c.d[i+1] += c.d[i]/10, c.d[i] %= 10;
        c.clean();
        return c;
    }
    bign operator / (const bign& b){
        int i, j;
        bign c = *this, a = 0;
        for (i = len - 1; i >= 0; i--)
        {
            a = a*10 + d[i];
            for (j = 0; j < 10; j++) if (a < b*(j+1)) break;
            c.d[i] = j;
            a = a - b*j;
        }
        c.clean();
        return c;
    }
    bign operator % (const bign& b){
        int i, j;
        bign a = 0;
        for (i = len - 1; i >= 0; i--)
        {
            a = a*10 + d[i];
            for (j = 0; j < 10; j++) if (a < b*(j+1)) break;
            a = a - b*j;
        }
        return a;
    }
    bign operator += (const bign& b){
        *this = *this + b;
        return *this;
    }

    bool operator <(const bign& b) const{
        if(len != b.len) return len < b.len;
        for(int i = len-1; i >= 0; i--)
            if(d[i] != b.d[i]) return d[i] < b.d[i];
        return false;
    }
    bool operator >(const bign& b) const{return b < *this;}
    bool operator<=(const bign& b) const{return !(b < *this);}
    bool operator>=(const bign& b) const{return !(*this < b);}
    bool operator!=(const bign& b) const{return b < *this || *this < b;}
    bool operator==(const bign& b) const{return !(b < *this) && !(b > *this);}

    string str() const{
        char s[maxn]={};
        for(int i = 0; i < len; i++) s[len-1-i] = d[i]+'0';
        return s;
    }
};

istream& operator >> (istream& in, bign& x)
{
    string s;
    in >> s;
    x = s.c_str();
    return in;
}

ostream& operator << (ostream& out, const bign& x)
{
    out << x.str();
    return out;
}
int main()
{
    bign a[105],mul,chu;
    a[1]=1;
    int i,j,n;
    for(i=2;i<=100;i++) {
        mul=(4 * i - 2);chu=(i + 1);
        a[i] = a[i - 1] * mul / chu;
    }
    while(cin>>n){
        cout<<a[n]<<endl;
    }

    return 0;
}

C.
写着让用queue没看出跟queue啥关系,写了一发spfa
(好吧,听说是想让你bfs,这时候就会用到队列…)

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
struct edge{
    int to,cost;
};
int main()
{
    int n,a,b,s,i,j,k;
    while(cin>>n&&n) {
        cin>>a>>b;
        vector<edge>G[205];
        bool inque[205]={0};
        int dis[205];memset(dis,0x3f,sizeof(dis));
        //for (i = 1; i <= n; i++)dis[i] = (1 << 31) - 1;
        dis[a] = 0;
        for (i = 1; i <= n; i++) {
            cin>>j;
            if(i+j<=n)G[i].push_back(edge{i+j,1});
            if(i-j>0)G[i].push_back(edge{i-j,1});
        }
        queue<int> que;
        que.push(a);
        //inque[s] = true;
        while (!que.empty()) {
            int t = que.front();
            que.pop();
            inque[t] = false;
            for (i = 0; i < G[t].size(); i++) {
                edge e = G[t][i];
                if (dis[e.to] > dis[t] + e.cost) {
                    dis[e.to] = dis[t] + e.cost;
                    if (!inque[e.to]) {
                        que.push(e.to);
                        inque[e.to] = true;
                    }
                }
            }
        }
        if(dis[b]==0x3f3f3f3f)
            puts("-1");
        else cout<<dis[b]<<endl;
    }

    return 0;
}

D.
智商检测题

#include<cstdio>
#include<iostream>
#include<string>
#include<map>
using namespace std;
int main()
{
    int n,i,j,k;
    while(cin>>n&&n){
        string str;map<string,int>mp;
        while(n--){
            cin>>str;mp[str]++;
        }
        map<string,int>::iterator iter;
        int maxn=0;string ans;
        for(iter=mp.begin();iter!=mp.end();iter++){
            if(iter->second>maxn){
                maxn=iter->second;ans=iter->first;
            }
        }
        cout<<ans<<endl;
    }

    return 0;
}

E.
稍复杂的智商检测,map里面嵌套map

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
int main()
{
    int n,i,j,k;
    cin>>n;
    for(i=1;i<=n;i++){
         typedef map<string,map<string,int> > Map;
         Map mp;
         int m;cin>>m;
         for(j=1;j<=m;j++){
             string str1,str2;int a;cin>>str1>>str2>>a;

             mp[str2][str1]+=a;
         }
         Map::iterator iter;map<string,int>::iterator iter1;
         for(iter=mp.begin();iter!=mp.end();iter++){
             cout<<iter->first<<endl;
             for(iter1=iter->second.begin();iter1!=iter->second.end();iter1++){
                 cout<<"   |----"<<iter1->first<<'('<<iter1->second<<')'<<endl;
             }
         }
         if(i!=n)puts("");
    }

    return 0;
}

F.HDU 4302
好多做法都能水过去,写了个multiset+模拟,题目本身不难但是若干小细节需要注意,容易写挂

#include<cstdio>
#include<iostream>
#include<set>
#include<algorithm>
#include<cmath>
using namespace std;
int main()
{
    int T,i,j,k,cnt=0;
    cin>>T;
    while(T--){
        cnt++;
        multiset<int>s;multiset<int>::iterator iter,iter1;
        int pos=0,dir=1,len=0;
        int l,n;cin>>l>>n;
        while(n--){
            scanf("%d",&i);
            if(!i) {
                scanf("%d", &j);
                s.insert(j);
                //for(auto a:s)cout<<a<<' ';cout<<endl;
            }
            else{
                if(s.empty())continue;//没蛋糕的时候原地不动
                //cout<<pos<<endl;
                iter=s.lower_bound(pos);//找最近的蛋糕
                if(iter==s.end()){
                    iter--;
                    dir=0;len+=abs(pos-(*iter));pos=(*iter);s.erase(iter);
                }
                else if(iter==s.begin()){
                    len+=abs(pos-(*iter));pos=(*iter);dir=1;s.erase(iter);
                }
                else{//如果被两块蛋糕夹在中间,需要讨论
                    iter1=--iter;iter++;
                    if(abs(pos-(*iter1))<abs(pos-(*iter))){//如果一侧距离更短
                        dir=0;len+=abs(pos-(*iter1));pos=(*iter1);s.erase(iter1);
                    }
                    else{
                        if(abs(pos-(*iter1))==abs(pos-(*iter))){//如果距离相等,看方向
                            if(dir==0){
                                len+=abs(pos-(*iter1));pos=(*iter1);
                                if((*iter1)<pos)dir=0;s.erase(iter1);
                            }
                            else{
                                if((*iter)>pos)dir=1;
                                len+=abs(pos-(*iter));pos=(*iter);s.erase(iter);
                            }
                        }
                        else{//距离不等
                            if((*iter)>pos)dir=1;
                            len+=abs(pos-(*iter));pos=(*iter);s.erase(iter);
                        }
                    }
                }
            }
        }
        printf("Case %d: %d\n",cnt,len);
    }

    return 0;
}

G.HDU 6109
这道题有两个关系,相等关系和不等关系,相等关系有传递性,这个很容易想到并查集这个数据结构来表示这个关系,然后是不等关系,这个因为没有传递关系,所以不能用并查集来表示。这道题一开始我想到的是挑战程序设计书上的食物链那道题,想要用unite(a,b+MAXN),unite(a+MAXN,b)来表示不等关系,这种方法是错的,这种方法是当a,b表示不在同一集合,并且集合种类只有两个的时候才是这样做法。 原因是,那道题中两个元素如果不是相等,那么肯定就是反面,也就是一个元素可以跟另一个元素的反面合并。而在这题中,如果对于a,b,c有a!=b,b!=c,那么把a,c合并到一起肯定是有问题的。
可以考虑用set来记录不等,当a!=b的时候,给a的set压入b,给b的set压入a,这样当询问a和某个数是否有不相等关系的时候,查一查a的set里面有没有这个数。
考虑set的方法和并查集结合起来,当a和b合在同一个集合s中的时候,如果把a的set和b的set的信息都添加到s的set里面。考虑以下这种情况,a!=c,b!=d,a==b,那么c和b是肯定不相等的,当a和b合并集合的时候,按上面说的那种想法,那么此时find(b)的set里面一查就有c这个数了,这样就能判断出c这个数不等于b了。具体的实现是在并查集的find函数里面加一个pushup函数,类似与线段树的pushup,把儿子的信息合并到父亲身上,这种时间复杂度因为并查集的路径压缩可以很小。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e5+5;
int par[N];
set<int>s[N];int cnt;
void init(void)
{
    for(int i=1;i<N;i++)
        par[i]=i,s[i].clear();
    cnt=0;
}
void pushup(int x,int fa)//路径压缩的时候把这个点的所有不等信息合并到他的祖先那里去
{
    s[fa].insert(s[x].begin(),s[x].end());
}
int find(int x)
{
    if(x==par[x])
        return x;
    pushup(x,par[x]);//信息上推
    return par[x]=find(par[x]);
}
void unite(int x,int y)
{
    x=find(x),y=find(y);
    if(x==y)return;
    if(s[x].size()<s[y].size())//优化,类似按秩合并
        par[x]=y;
    else {
        par[y]=x;
    }
}
int main()
{
    int l,i,j,e,k;
    vector<int>ans;
    cin>>l;init();
    while(l--){
        cnt++;
        scanf("%d%d%d",&i,&j,&e);
        i=find(i);j=find(j);//注意一定要把find的值赋值给原数
        if(!e){
            if(i==j){
                ans.push_back(cnt);
                init();
                continue;
            }
            s[i].insert(j);s[j].insert(i);
        }
        else{
            if(s[i].count(j)||s[j].count(i)){
                ans.push_back(cnt);
                init();continue;
            }
            unite(i,j);
        }
    }
    cout<<ans.size()<<endl;
    for(i=0;i<ans.size();i++)
        cout<<ans[i]<<endl;

    return 0;
}

H,I本蒟蒻暂时不会,flag在此。

J. POJ 3275
题目要求的是最少需要知道多少关系能够保证100%知道剩下所有关系的情况,因此我们应该尽量多的根据已有条件推断牛之间的关系,而剩下的情况从最糟糕的角度看,应该是每一对关系都需要问询一次才能得知。
而为了推断已有关系,可以类比有向图最短路来做,利用floyd。但是朴素的floyd O(n^3)的复杂度太高了,所以要用邻接表优化一下。

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
    int n,m,i,j,k;
    bool issure[1005][1005]={0};
    vector<int>from[1005],to[1005];
    cin>>n>>m;
    for(i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        from[x].push_back(y);to[y].push_back(x);issure[x][y]=true;//比x小的关系,比y大的关系
    }
    for(k=1;k<=n;k++)
        for(i=0;i<to[k].size();i++)
            for(j=0;j<from[k].size();j++){
        int u,v;
        u=to[k][i],v=from[k][j];//k是用来松弛的点,注意字母顺序!!
        if(!issure[u][v]) {//注意字母顺序!!
            issure[u][v] = true;from[u].push_back(v);to[v].push_back(u);
        }
    }
    int res=0;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++) {
            if (i == j)continue;
            if (issure[i][j])
                res++;
        }
    cout<<n*(n-1)/2-res<<endl;

    return 0;
}

K. Hiho 1513
一个蛮神奇的bitset的应用,当然,其实也可能是因为我自己本来就不大会bitset,所以看起来挺神奇的。
思路:首先设法将每门科目的排名列出,开一个二维的bitset数组,根据每门科目的排名利用&运算,求出所有科目都比当前人名次靠前的同学的人数。

大致思想就是,对于每个人的每一科,预处理出这一科比他高的人,并在代表那个人的编号的位上记为1 ,然后对于每个人,把他5科排名的bitset &起来,就可以得到所有科都比他高的人的数目了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<bitset>
#include<algorithm>
using namespace std;
const int N=3e4+5;
bitset<N>high[5][N],ans;//high表示第j科排名第i的人前面的名次的位置
int mark1[5][N],stu[5][N];//mark记录第j科排名第i的人的编号,stu表示第j科编号为i的人的名次
int main()
{
    int n,i,j,k;
    cin>>n;
    for(i=1;i<=n;i++)
        for(j=0;j<5;j++){
        int t;scanf("%d",&t);
        mark1[j][t]=i;stu[j][i]=t;
    }
    for(j=0;j<5;j++)
        for(i=1;i<=n;i++){
        if(i==1)high[j][i]=0;
        else{
            high[j][i]=high[j][i-1];
            high[j][i].set(mark1[j][i-1]);
        }
    }
    for(i=1;i<=n;i++){
        ans=high[0][stu[0][i]];
        for(j=1;j<5;j++)
            ans&=high[j][stu[j][i]];
        cout<<ans.count()<<endl;
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值