czl蒟蒻的XJOI类型训练1

->XJOI类型训练1<-

T1-基数排序

包含知识点-基数排序法
典型题目:

知识点讲解:

基数排序的核心在于把所需要排序的从最高位开始,把每一个数的每一位的值放到一个桶里,然后以更新过的顺序进行取出。基数排序属于稳定排序,复杂度为O (n*log(r)*m),r为所采取的基数,m为堆数。

代码实现:
#include<bits/stdc++.h>
using namespace std;

int n;
int a[1000005];

int get_len(int n)
{
    int cnt=0;
    while(n!=0)
    {
        cnt++;
        n/=10;
    }
    return cnt;
}

void _sort(int a[],int len, int n)
{
    int *tmp =new int[n];
    int *count =new int[10];
    int i,j,k;
    int r = 1;
    for(i = 1; i <= len; i++)
    {
        for(j = 0; j < 10; j++)
            count[j] = 0;
        for(j = 0; j < n; j++)
        {
            k = (a[j] / r) % 10;
            count[k]++;
        }
        for(j = 1; j < 10; j++)
            count[j] = count[j - 1] + count[j];
        for(j = n - 1; j >= 0; j--)
        {
            k = (a[j] / r) % 10;
            tmp[count[k] - 1] = a[j];
            count[k]--;
        }
        for(j = 0; j < n; j++)
            a[j] = tmp[j];
        r= r * 10;
    }
    delete[]tmp;
    delete[]count;
}

int main()
{
    int n;
    scanf("%d",&n);
    int maxnum=0;
    for(int i=0; i<n; i++)
    {
        scanf("%d",&a[i]);
        maxnum=max(maxnum,a[i]);
    }
    int len=get_len(maxnum);
    _sort(a,len,n);
    for(int i=0; i<n; i++)printf("%d ",a[i]);
}

T2-哈夫曼树

包含知识点-哈夫曼树的构造
典型题目:

洛谷:P1090 合并果子

知识点讲解:

一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树。哈夫曼树的特点在于权值较大的点在离根节点较近的位置。所以我们可以用一个优先队列来解决。
先把所有的节点看成根节点,把图变成一个森林,每次从这片森林中选取最小的两个节点,合并成一棵树,将两个节点的权值之和作为新生成的树的权值,并删除原来的两个节点,直到这个森林变成一棵树为止,生成的便是哈夫曼树。

代码实现:
#include<bits/stdc++.h>
using namespace std;
#define maxn 10005
typedef long long ll;

priority_queue<ll>que;
ll n;
ll a[maxn];
ll x,y,res;

int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        a[i]=-a[i];
        que.push(a[i]);
    }
    for(int i=1;i<n;i++)
    {
        x=que.top();
        que.pop();
        y=que.top();
        que.pop();
        res-=x+y;
        que.push(x+y);
    }
    printf("%lld\n",res);
}

T3-无向图的连通分量

包含知识点-并查集
典型题目:

HDU1213:How Many Tables

知识点讲解:

在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中,这时候并查集就会发挥很好的作用。
主要操作:
初始化——把每个点所在集合初始化为其自身。通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。
查找——查找元素所在的集合,即根节点。
合并——将两个元素所在的集合合并为一个集合。通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。
并查集还可以通过路径压缩把图压缩成菊花图,能够应用于很多题目中。

代码实现:
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;

int fa[maxn],n,m;
int sum;

void init()
{
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
    }
}

int find(int x)
{
    if(fa[x]==x)return x;
    else return fa[x]=find(fa[x]);
}

void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)return;
    fa[x]=y;
    sum-=1;
}

int main()
{
    scanf("%d%d",&n,&m);
    sum=n;
    init();
    for(int i=1;i<=m;i++)
    {
        int s,t;
        scanf("%d%d",&s,&t);
        unite(s,t);
    }
    printf("%d\n",sum);//输出的是连通分量的个数
}

T4-奇数幻方

包含知识点-幻方
典型题目:

洛谷P2615:神奇的幻方

知识点讲解:

N阶幻方要求满足在N*N的格子中填入1~N*N这些数,满足每一行,每一列,每一个对角线的和都相等。
操作的方法有很多,可以爆搜,但显然是太慢了,所以有一种可以完成奇数幻方的快速方法。现在第一行的最中间填上数字1,然后每次把指针向右上移动一格,填上下一个数。如果下一个格子超出了范围,那么就加上N然后对N去模,如果这一个格子已经有数了,就在原来格子下面填上下一个数。(但是蒟蒻不知道原理,大佬可以在下面发表~)

代码实现:
#include<bits/stdc++.h>
using namespace std;

int n;
int f[1005][1005]={0};

void get_f(int i,int j,int now)
{
    f[i][j]=now;
    int x=i,y=j;
    if(now==n*n)return ;
    if(i-1>=1)
    {
        i-=1;
    }
    else
    {
        i=n;
    }
    if(j+1<=n)
    {
        j+=1;
    }
    else
    {
        j=1;
    }
    if(f[i][j]==0)get_f(i,j,now+1);
    else
    {
        if(x+1<=n)x+=1;
        else x=1;
        get_f(x,y,now+1);
    }
}

int main()
{
    scanf("%d",&n);
    memset(f,0,sizeof f);
    get_f(1,(n+1)/2,1);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        printf("%d ",f[i][j]);
        if(j==n)printf("\n");
    }
    return 0;
}

T5-最近点对

包含知识点-分治
典型题目:

洛谷P2615:平面最近点对(加强版)

知识点讲解:

在二维平面上的n个点中,如何快速的找出最近的一对点,就是最近点对问题。 一种简单的想法是暴力枚举每两个点,记录最小距离,显然,时间复杂度为O(n^2)。
其实,这里用到了分治的思想。,就可以把复杂度降到O(n*log(n)*log(n))将所给平面上n个点的集合S分成两个子集S1和S2,每个子集中约有n/2个点。然后在每个子集中递归地求最接近的点对。在这里,一个关键的问题是如何实现分治法中的合并步骤,即由S1和S2的最接近点对,如何求得原集合S中的最接近点对。如果这两个点分别在S1和S2中,问题就变得复杂了。
为了使问题变得简单,首先考虑一维的情形。此时,S中的n个点退化为x轴上的n个实数x1,x2,…,xn。最接近点对即为这n个实数中相差最小的两个实数。显然可以先将点排好序,然后线性扫描就可以了。但我们为了便于推广到二维的情形,尝试用分治法解决这个问题。
假设我们用m点将S分为S1和S2两个集合,这样一来,对于所有的p(S1中的点)和q(S2中的点),有p<q。递归地在S1和S2上找出其最接近点对{p1,p2}和{q1,q2},并设d=min{ |p1-p2| , |q1-q2| }由此易知,S中最接近点对或者是{p1,p2},或者是{q1,q2},或者是某个{q3,p3}。
如果最接近点对是{q3,p3},即|p3-q3|<d,则p3和q3两者与m的距离都不超过d,且在区间(m-d,d]和(d,m+d]各有且仅有一个点。这样,就可以在线性时间内实现合并。此时,一维情形下的最近点对时间复杂度为O(n*log(n))。
在二维情形下,类似的,利用分治法。形成宽为2d的带状区间,最多可能有n个点,合并时间最坏情况下为n^2,。但是,P1和P2中的点具有以下稀疏的性质,对于P1中的任意一点,P2中的点必定落在一个d X 2d的矩形中,且最多只需检查六个点(鸽巢原理)。
这样,先将带状区间的点按y坐标排序,然后线性扫描,这样合并的时间复杂度为O(n*log(n)),几乎为线性了。

代码实现:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;


const int N = 200010;
const double EPS = 1E-10, INF = 1.0 / 0.0;

inline int Sgn(double a) {
    return (a < -EPS) ? (-1) : (a > EPS);
}

inline bool Smaller(double a, double b) {
    return (Sgn(fabs(a - b))) ? (a < b) : (0);
}

int n;

struct Point {
    double x, y;
    double Dis(Point &a) {
        return sqrt(pow(x - a.x, 2) + pow(y - a.y, 2));
    }
    bool operator < (const Point &a) const {
        return (Sgn(fabs(y - a.y))) ? (y < a.y) : (x < a.x);
    }
    bool operator > (const Point &a) const {
        return (Sgn(fabs(y - a.y))) ? (y > a.y) : (x > a.x);
    }
} p[N];

double Dis(Point &a, Point &b) {
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

bool Cmpxy(Point a, Point b) {
    return (Sgn(fabs(a.x - b.x))) ? (a.x < b.x) : (a.y < b.y);
}

Point temp[N];

void Merger(int l, int m, int r) {
    int i = l, j = m + 1, k = l;
    while (i <= m && j <= r) {
        if (p[i] < p[j]) temp[k++] = p[i++];
        else temp[k++] = p[j++];
    }
    while (i <= m) temp[k++] = p[i++];
    while (j <= r) temp[k++] = p[j++];
    for (int i = l; i <= r; ++i)
        p[i] = temp[i];
    return;
}

int t[N];

double Divide(int l, int r) {
    if (l == r) return INF;
    if (l + 1 == r) {
        if (p[l] > p[r]) swap(p[l], p[r]);
        return Dis(p[l], p[r]);
    }
    int m = (l + r) >> 1;
    double lx = p[m].x, d1 = Divide(l, m), d2 = Divide(m + 1, r), d = min(d1, d2);
    int tk = 0;
    for (int i = l; i <= r; ++i)
        if (Smaller(fabs(p[i].x - lx), d)) t[++tk] = i;
    for (int i = 1; i <= tk; ++i)
        for (int j = i + 1; j <= tk && Smaller(p[t[j]].y - p[t[i]].y, d); ++j)
            d = min(d, Dis(p[t[i]], p[t[j]]));
    Merger(l, m, r);
    return d;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%lf%lf", &p[i].x, &p[i].y);
    sort(p + 1, p + n + 1, Cmpxy);
    printf("%.2lf\n", Divide(1, n));
    return 0;
}
//贴了本OJ大佬的代码~

T6-神奇的供水系统

包含知识点-深度优先搜索(DFS)
典型题目:

洛谷P1562:还是N皇后

知识点讲解:

深度优先遍历图的方法是,从图中某顶点v出发:
(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
深度优先搜索比较的节省空间,但是相应的,会花费比较多的时间。相应的,广度优先搜索会节省一些时间,但会占用很多空间。
这题由于供水系统的集水器只有在积满水之后才会向下传水,所以是一道很经典的DFS提,只需要进行一次深搜即可。

代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int mx[N],sum[N],t[N],S,h,t1,n,m,s,T,k,v1;
vector <int> e[N];
void dfs(int x){
    for (int i=0;i<e[x].size();++i){
        int to=e[x][i]; dfs(to);
        mx[x]=max(mx[x],mx[to]+sum[to]*t[to]); sum[x]+=sum[to];
    }
}
int main(){
    scanf("%d%d%d%d",&n,&m,&s,&T);
    for (int i=1;i<=m;++i){
        scanf("%d%d%d%d",&S,&h,&k,&t1);
        v1=min(S*h,s*T*S); mx[k]=max(mx[k],v1*t1); sum[k]+=v1;
    }
    for (int i=1;i<n;++i){
        scanf("%d%d",&k,&t[i]); e[k].push_back(i);
    } dfs(n); printf("%d",mx[n]);
}

T7-电话连线

包含知识点-最小生成树
典型题目:

洛谷P3366:【模板】最小生成树

知识点讲解:

最小生成树是最小权重生成树的简称,简单来说,就是给你一个带权无向图,然后让你通过选取这个图的几条边,使这些点连通,并且权值之和最小,这样生成的树就是最小生成树。
这题是个模板,可以用Kruskal或者Prim算法进行计算,这两种算法将在后面进行讲解。这里的代码是Kruskal算法,如果是新手的话建议使用Kruskal算法,因为比较好理解,不过最终这两种都还是要学的~

代码实现:
#include<bits/stdc++.h>
using namespace std;
#define maxn 105
int fa[105];

struct edge
{
    int from;
    int to;
    int c;
} e[100050];

int n,tot=0;
int cost,ans=0,m=0;
bool vis[maxn][maxn];

inline void init()
{
    for(int i=1; i<=n; i++)
    {
        fa[i]=i;
    }
}

int find(int x)
{
    if(fa[x]==x)
    {
        return x;
    }
    else
    {
        return fa[x]=find(fa[x]);
    }
}

void unite(int x,int y)
{
    x=find(x);
    y=find(y);
//  printf("unite:%d %d\n",x,y);
    if(x==y)return ;
    fa[x]=y;
}

bool cmp(edge a,edge b)
{
    return a.c<b.c;
}

bool same(int x,int y)
{
    x=find(x);
    y=find(y);
//  printf("same:%d %d\n",x,y);
    if(x==y)return true;
    return false;
}

int main()
{
//  freopen("out.txt","w",stdout);
    memset(vis,true,sizeof vis);
    scanf("%d",&n);
    init();
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            scanf("%d",&cost);
            if(vis[i][j]==true)
            {
                if(cost==0)
                {
//                  printf("%d %d\n",i,j);
                    unite(i,j);
                    vis[i][j]=false;
                    vis[j][i]=false;
                }
                else
                {
                    tot++;
                    e[tot].from=i;
                    e[tot].to=j;
                    e[tot].c=cost;
                    vis[i][j]=false;
                    vis[j][i]=false;
                }
            }
        }
    }

//  for(int i=1;i<=n;i++)printf("%d->%d\n",i,fa[i]);
    sort(e+1,e+1+tot,cmp);
    for(int i=1; i<=tot; i++)
    {
        edge x=e[i];
//      printf("%d %d %d\n",x.from,x.to,x.c);
//      printf("%d %d\n",find(x.from),find(x.to));
        if(same(x.from,x.to)==false)
        {
            unite(x.from,x.to);
            m++;
            ans+=x.c;
        }
    }
//  for(int i=1;i<=n;i++)printf("%d->%d\n",i,fa[i]);
    printf("%d\n%d",m,ans);
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值