一周算法游戏(lab_oj)

本博客根据相关的例题,完成相应的算法笔记

级 数 求 和

Description

已知 Sn = 1 + 1/2 + 1/3 + ... + 1/n. 显然对于一个任意整数K,当n足够大的时候,Sn 大于 K。

现给出一个整数 K(1≤K≤20), 要求计算出一个最小的 n,使得 Sn > K.

Input

一个正整数 K

Output

一个正整数 N

Sample Input 1

1

Sample Output 1

2
#include <bits/stdc++.h>
using namespace std;
int main() {
    int k,n=0;
    scanf("%d",&k);
    for(double Sn=0;Sn<=k;++n,Sn+=1.0/n);
    printf("%d",n);
    return 0;
}

这题比较简单,没有解释

 

女 装 Plan

zjj非常rich,但是最近手头只有两种面值的金币,两种面值均为正整数且彼此互素。为了让实验室成员都有女装穿,zjj准备了 无数 的金币。可是在不找零的情况下,有些女装是无法准确支付的,现在zjj想知道,在无法准确支付的女装中,最贵的价值多少金币

注意:输入数据保证存在无法准确支付的女装

 

Input

两个正整数 a 和 b,它们之间用一个空格隔开,表示zjj手中金币的面值

ouput

一个正整数 N,表示zjj在不找零的情况下无法准确支付的最贵的女装价值

 

sample input 3 7

sample ouput 11

注意

//我在博客中曾经写过关于 扩张欧几里得算法
//定理1:给定两个非零整数 a和b,求一组整数解(x,y)
//使得ax+by=gcd(a,b)成立,其中gcd(a,b)表示a和b的最大公约数

//定理2: 
//对于正整数p , q满足gcd(p, q) = 1, 
//我们有px + qy = n 不能实现的最大正整数n 为pq - p - q .

//两者的不同点在于定理1 p,q未知,求存在解x,y
//定理2:p,q已经知道,求px+qy不能实现的最大值 


//这个题目中给的数据都是互质的,如果不是互质的那么结果为1
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
	long long a,b;
	cin>>a>>b;
	long long ans=a*b-a-b;
	cout<<ans;
	return 0;
}

手办迁居

作为cdd的mx打算把已经装箱的手办全都带回家,并且按照单推程度分成了不同的堆,现在mx想把各堆的手办箱子都合成一堆。
每一次合并,mx可以把两堆手办合并到一起,消耗的体力等于两堆手办的重量之和,可以看出,经过n-1次合并后就变成一堆了。mx在合并手办时消耗的总体力等于每次所耗体力之和。

因为mx实在太弱了,所以想尽可能节省体力,假定每箱手办重量都为 1 ,并且已知堆的数量和每堆手办的数量。请你帮mx算算他最少只需要花费多少体力

input

共两行

第一行是一个整数 n,表示手办的堆数

第二行包括 n 个整数,用空格分隔,第 i 个整数 ai 是第 i 堆手办的数目

1<ai<20000

output

一个整数,即最小的体力耗费,数据保证这个值小于2^31
 

sample input

3
1 2 9

sample output

15

#include <iostream>
#include  <stdio.h>
#include <vector>
#include <queue> 
using namespace std;
int main()
{
	priority_queue<long long  , vector<long long > , greater<long long > > q;
	//优先队列默认事less<int>是降序,所以使用greater<int>作为升序 
	int m;
	long long  num;
	long long sum;
	cin>>m;
	sum = 0;
	while(m--)
	{	
		cin>>num;
		q.push (num);
	}
	while(q.size()!=1)//用于记录优先队列的长度,当只剩最后一个数的时候就停止 
	{
		num = q.top();
		q.pop ();
		num += q.top ();
		q.pop ();
		q.push (num);
		sum += num;
	}
	cout<<sum << endl;
	return 0;
}        

社恐的朋友圈

YouChat最近开启了一个新功能,可以让朋友的朋友成为你的朋友,但是有社交障碍的mx很难判断他跟谁有朋友关系,他给了你一个朋友关系图,请你帮他看看任意两个人是不是朋友

例如:x和y是朋友,y和z是朋友,那么x和z也是朋友;如果x,y是朋友,那么x的朋友都是y的朋友,y的朋友都是x的朋友

input

第一行包含3个整数 n,m,k 分别表示有 n 个人,m 个朋友关系,询问 k 对朋友关系

接下来 m 行:每行两个数 Mi,Mj 表示 Mi 和 Mj 具有朋友关系

接下来 k 行:每行两个数 Ki,Kj 询问 Ki 和 Kj 是否有朋友关系

 

output

接下来 k 行:每行两个数 Ki,Kj 询问 Ki 和 Kj 是否有朋友关系

 

sample input

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

sample output

Yes

Yes

No

这题的思想是借助并查集的方式解决问题

并查集有相应的模块

并查集的应用十分广泛,包括一些算法,当应用上并查集的时候,也会更容易实现。下面总结下并查集的相关内容

个人的理解是:并查集就是对集合三种常用操作的再一次抽象。分别是集合的合并(Union)、元素的搜索(Find)和对集合的分解。因为这3中操作非常常用并且又不囿于集合,所以就把这一组操作抽象成一个独立的数据结构。
标准定义

int unionsearch(int root) //查找根结点
{
	int son, tmp;
	son = root;
	while(root != pre[root]) //我的上级不是掌门
		root = pre[root];
	while(son != root) //我就找他的上级,直到掌门出现
	{
		tmp = pre[son];
		pre[son] = root;
		son = tmp;
	}
	return root; //掌门驾到~~
}

 

 

下面是题目代码

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int mark[5005],fa[5005];
int get(int x){
	if(fa[x]==x)return x;
	return fa[x]=get(fa[x]);
}
int main(){
	int n,m,p;cin>>n>>m>>p;
	int a,b; 
    memset(mark,0,sizeof(mark));
	for(int i=1;i<=n;i++)fa[i]=i;//初始化设置 
	while(m--){//输入m组朋友关系 
		cin>>a>>b;// 
		a=get(a);//获得a和谁是朋友关系
		b=get(b);
		if(a!=b){
			fa[a]=b;
		}
	}
	for(int i=1;i<=p;i++){
		cin>>a>>b;
		a=get(a);b=get(b);
		if(a==b)mark[i]=1;
    }
    for(int i=1;i<=p;i++){
        if(mark[i])cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
	}
    return 0;
}

LifeCanAche

给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先

 

第一行包括3个正整数 N M S 分别表示树的结点的个数、询问的个数和树根结点的序号

接下来 N-1 行每行包含两个正整数 x,y 表示 x与y 之间有一条直接连接的边(数据保证可以构成树)

接下来M行每行包含2个正整数 a,b 表示询问 a和b 的最近公共祖先
题目保证,N≤10000,M≤10000

题目链接http://oj.nhjc.ac.cn/contest/5/problem/E

#include <bits/stdc++.h>
#define N 500005
#define pb push_back
using namespace std;
vector<int>que[N];
struct node
{
    int next,to;
}edge[N<<2];
int head[N<<2],cnt,y,u[N],v[N],dad[N],n,m,root,fa[N],ans[N];
void add(int u,int v)
{
    edge[++cnt].next=head[u];
    edge[cnt].to=v;
    head[u]=cnt;
}
int read()
{
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) 
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
int find_(int x) {return x==fa[x]?x:fa[x]=find_(fa[x]);} 
void dfs(int x)
{
    fa[x]=x;
    for(int i=head[x];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(dad[x]!=v)
        {
            dad[v]=x;
            dfs(v);
        }
    }
    for(int i=0;i<que[x].size();i++)if(dad[y=x^u[que[x][i]]^v[que[x][i]]]) ans[que[x][i]]=find_(y);
    fa[x]=dad[x];
}
int main()
{
    n=read();
    m=read();
    root=read();
    int x,y;
    for(int i=1;i<n;i++)
    {
        x=read();y=read();
        add(x,y);
        add(y,x);
    }
    for(int i=1;i<=m;i++)
    {
        u[i]=read();v[i]=read();
        que[u[i]].pb(i);
        que[v[i]].pb(i);
    }
    dfs(root);
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欲游山河十万里

你的鼓励是我们创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值