队内训练第一周

涉及的知识点

本周练习主要涉及基本数据结构、树的直径、LCA入门、倍增、树状数组、RMQ、差分

已完成题目 POJ: 3253、3264、2492、2421;HDU:1251、1213、CodeForces 1467A、977D、LibreOJ 2421

未完成题目 POJ: 1330、2631、1125、1195、2352、2299、3368;HDU:1394、4825、3183;LightOJ 1113、计蒜客A1633、CodeForces: 1000C、1467B、920E、1473B

已完成题目整理

POJ 3253

题目大意:给出N块木板,每合并两块木板所需要的花费为两木板长度之和,求出合并完成时的最小花费

思路:哈夫曼树的变形用法,每两次选取最小的两块板合并成一块新的板,累和,循环操作(如果取的不是最小值,花费必定不为最小)

代码

#include <iostream>
#include <queue>
using namespace std;
priority_queue<int,vector<int>,greater<int>>Q;
int N,L;
long long ans;
int main()
{
    cin >>N;
    for(int i=0;i<N;i++)//数据录入
    {
        cin >>L;
        Q.push(L);
    }
    while(Q.size()!=1)//最后一个数据不需要
    {
        int a=Q.top();
        Q.pop();
        int b=Q.top();
        Q.pop();
        ans+=a+b;//累和
        Q.push(a+b);
    }
    cout <<ans;
    return 0;
}

POJ 3264

题目大意:给出一个乱序序列,每次给出一个范围L~R的询问,求出该范围内最大值与最小值的差

思路:RMQ问题,采用线段树的模型来解决,构造两棵树

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
int N,Q,A,B,cowMax[212121],cowMin[212121],n=1;
void update(int k,int x)//更新操作,构造线段树
{
    k+=n-1;//从叶子节点开始建树,n必须是2的整数次幂,类似堆的插入
    cowMax[k]=x;
    cowMin[k]=x;
    while(k)
    {
        k>>=1;
        cowMax[k]=max(cowMax[2*k],cowMax[2*k+1]);
        cowMin[k]=min(cowMin[2*k],cowMin[2*k+1]);
    }
}
int queryMax(int k,int l,int r)//查询区域最大值
{
    if(r<A||B<l)return 0;//如果查询区域与执行区域不相交,返回预设值,不能取等号,否则等号的值可能会在递归中无法取到
    if(A<=l&&r<=B)return cowMax[k];//如果查询区域完全包含执行区域,返回执行区域最值
    return max(queryMax(k*2,l,(l+r)/2),queryMax(k*2+1,(l+r)/2+1,r));//否则取左执行区域与右执行区域比较后最值
}
int queryMin(int k,int l,int r)//查询区域最小值
{
    if(r<A||B<l)return INT_MAX;
    if(A<=l&&r<=B)return cowMin[k];
    return min(queryMin(k*2,l,(l+r)/2),queryMin(k*2+1,(l+r)/2+1,r));
}
int main()
{
    scanf("%d%d",&N,&Q);
    while(n<N)
        n<<=1;
    for(int i=1; i<=2*n; i++)
        cowMin[i]=INT_MAX;
    for(int i=1; i<=N; i++)
    {
        int x;
        scanf("%d",&x);
        update(i,x);
    }
    while(Q--)
    {
        scanf("%d%d",&A,&B);
        if(A!=B)
            printf("%d\n",queryMax(1,1,n)-queryMin(1,1,n));
        else
            printf("0\n");
    }
    return 0;
}

POJ 2492

题目大意:有N只虫子,每个个体只有雌和雄,给出数对性别相异的虫子对,判断是否有矛盾(同性配对)

思路:带权并查集,在一般的并查集基础上加上了相反关系,对于一个虫子(编号为x),假设一个它的对立性别的虫子编号为x+N,于是该问题被转换成简单的并查集问题

代码

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 1e6 + 7;
int f[20007];
int Find(int x){
    if (f[x] == x) return x;
    return f[x] = Find(f[x]);
}//路径压缩+寻找祖先
void mix(int u, int v){
    int x = Find(u), y = Find(v);
    if (x == y) return;
    f[x] = y;
}//合并
int main(){
    int t; scanf("%d", &t);
    int cas = 0;
    while (t --){
        int n, m; scanf("%d%d", &n, &m);
        for (int i=0; i<=2 * n; i++) f[i] = i;
        bool flag = false;//标记是否有异常
        for (int i=1; i<=m; i++){
            int u, v; scanf("%d%d", &u, &v);
            if (flag) continue;
            int x = Find(u), y = Find(v);//查找祖先(即判断是否同性)
            if (x == y) {
                flag = true; continue;
            }else{
                mix(u, v + n);//合并对应性别
                mix(v, u + n);
            }
        }
        printf("Scenario #%d:\n", ++cas);
        if (flag) printf("Suspicious bugs found!\n\n");
        else printf("No suspicious bugs found!\n\n");
    }
    return 0;
}

HDU 1251
题目大意:略

思路:map/unordered_map的简单应用,存储以每种前缀在词典中出现的次数,根据每次查询直接取值即可

代码

#include <iostream>
#include <unordered_map>
#include <cstring>
#include <cstdlib>
using namespace std;
unordered_map<string,int>UMap;
char str[12];
int main()
{
    while(gets(str))
    {
        int len=strlen(str);
        if(!len)
            break;
        string s;
        for(int i=0;i<len;i++)
        {
            s+=str[i];
            UMap[s]++;
        }
    }
    string t;
    while(cin >>t)
        cout <<UMap[t]<<endl;
    return 0;
}

POJ 2421

题目大意:有N个村庄,每个村庄有一定的距离,现在要修路使得总距离和最小,并且有些村庄已经修好了路

思路:最小生成树问题,去掉已经生成的边即可

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int Pre[12121],N,Q;
bool if_in[121][121];
typedef struct edge
{
    int a,b,value;
    edge(int _a,int _b,int dis)
    {
        a=_a;
        b=_b;
        value=dis;
    }
    bool operator <(edge x)const
    {
        return value>x.value;
    }
} edge;
int Seek(int x)
{
    if(Pre[x]==x)return x;
    return Pre[x]=Seek(Pre[x]);
}
bool Union(int a,int b)
{
    int fa=Seek(a),fb=Seek(b);
    if(fa==fb)
        return false;
    Pre[fa]=fb;
    return true;
}
priority_queue<edge>Que;
int main()
{
    cin >>N;
    for(int i=1; i<=N; i++)
    {
        Pre[i]=i;
        for(int j=1; j<=N; j++)
        {
            int dis;
            cin >>dis;
            if(i==j||if_in[i][j]||if_in[j][i])
                continue;
            if_in[i][j]=true;
            if_in[j][j]=true;
            Que.push(edge(i,j,dis));
        }
    }
    cin >>Q;
    while(Q--)
    {
        int a,b;
        cin >>a>>b;
        int fa=Seek(a),fb=Seek(b);
        if(fa!=fb)
            Pre[fa]=fb;
    }
    int ans=0,sum=0;
    while(!Que.empty()&&ans!=N-1)
    {
        edge tmp=Que.top();
        Que.pop();
        if(Union(tmp.a,tmp.b))
        {
            sum+=tmp.value;
            ans++;
        }
    }
    cout <<sum;
    return 0;
}

HDU 1213

题目大意:给出有N个人,两两之间有朋友,朋友的朋友为朋友,给出M对朋友,问有几个朋友群

思路:并查集

代码

#include <iostream>
#include <cstring>
using namespace std;
int pre[12121],T,N,M;
int Seek(int x)
{
    if(pre[x]==x)
        return x;
    return pre[x]=Seek(pre[x]);
}
void Union(int a,int b)
{
    int fa=Seek(a),fb=Seek(b);
    pre[fa]=fb;
}
int main()
{
    cin >>T;
    while(T--)
    {
        cin >>N>>M;
        for(int i=1; i<=N; i++)
            pre[i]=i;
        while(M--)
        {
            int a,b;
            cin >>a>>b;
            Union(a,b);
        }
        int ans=0;
        for(int i=1; i<=N; i++)
            if(pre[i]==i)
                ans++;
        cout <<ans<<endl;
    }
    return 0;
}

CodeForces 1467A

题目大意:给出一块电子屏幕,电子屏幕有给定的n个数位,一开始这些数位都为0,每过一秒自增到达10便循环,现在立刻停下某一位,而其相邻位需要隔一秒才能停下,以此类推,相邻的相邻需要两秒…求出完全停下时显示的最大值

思路:为确保最大,首位要确保最大,那么只需在第8秒时停下第二位即可,整个数最大

代码

#include <iostream>

using namespace std;
int T,N;
int main()
{
    cin >>T;
    while(T--)
    {
        cin >>N;
        if(N==1)
            cout <<"9";
        else if(N==2)
            cout <<"98";
        else
        {
            cout <<"989";
            N-=3;
            int i=9;
            while(N--)
                cout <<(++i)%10;
        }
        cout <<endl;
    }
    return 0;
}

LibreOJ 2421

题目大意:略

思路:DFS,对每个编号进行DFS,判断回到出发点需要几步,选取最少步数

#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
int object[212121],n,level,visit[212121],ans=212121;
int DFS(int x)
{
    int tmp=level+1;
    while(!visit[x])//以一个方向进行延伸
    {
        visit[x]=++level;//visit记录多少层,这个层数相对于当前的函数的参数x
        x=object[x];
    }
    if(tmp>visit[x])
        return 212121;
    return level-visit[x]+1;
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d",&object[i]);
    for(int i=1; i<=n; i++)
        if(!visit[i])ans=min(ans,DFS(i));
    cout <<ans<<endl;
    return 0;
}

CodeForces 977D

题目大意:给出n个数(乱序),对于其中任意一个数X,总能找到另一个数Y(除了首个数),使得X×2 = = == ==Y或者X%3 = = == == 0&&X/3 = = == == Y,求出符合这个关系的序列

思路:DFS,不过是从两个方向进行搜索

代码

#include <iostream>
#include <cstdlib>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
bool visited[121],finish;
ll N,data[121],ans;
deque<ll>Q;

void DFS(ll x)
{
    bool flag=false;
    if(ans==N-1)
    {
        Q.push_back(data[x]);
        while(Q.size()!=1)
        {
            printf("%lld ",Q.front());
            Q.pop_front();
        }
        printf("%lld",Q.front());
        Q.pop_back();
        finish=true;
        return;
    }
    for(int i=1; i<=N; i++)
    {
        if(finish)
            return;
        if(data[x]%3==0)
        {
            if(!visited[i]&&data[x]/3==data[i])
            {
                visited[i]=true;
                ans++;
                flag=true;
                Q.push_back(data[x]);
                DFS(i);
                visited[i]=false;
                ans--;
                Q.pop_back();

            }
        }
        if(!visited[i]&&data[x]*2==data[i])
        {
            visited[i]=true;
            ans++;
            if(!flag)
                Q.push_back(data[x]);
            DFS(i);
            visited[i]=false;
            ans--;
            if(!flag)
                Q.pop_back();
        }
        flag=false;
    }
}
int main()
{
    scanf("%lld",&N);
    for(int i=1; i<=N; i++)
        scanf("%lld",&data[i]);
    for(int i=1; i<=N; i++)
        if(!finish)
            DFS(i);
    return 0;
}

总结

完成量相对于总任务量还是不够的,主要是前期的各个知识点掌握不牢,训练也不够,准备巩固一遍上学期队里讲过的知识点,应该对眼下的境况有所帮助,未完成的题目在此记下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值