寒假训练第五周

寒假训练第五周

第一天

蓝桥杯训练赛

G.左孩子右兄弟

大意:
将多叉树通过左孩子有兄弟表示法转化成二叉树,问高度最高是多少。

思路:
最大高度=父节点孩子数+以它的孩子为父节点的子树的最大高度

代码:

#include<iostream>
#include<vector>
using namespace std;
vector<int> f[100050];//f[i]容器中装的是以i结点为父亲的所有孩子结点
int dfs(int node) {
	int count = 0;//存储每一层的孩子的最大孩子数目
	if (f[node].size() == 0) return 0; //递归出口, 似乎也可以不用这一句,到最后一层 count和f[node].size()都为0
	for (int i = 0; i < f[node].size(); i++) {
		count = max(count, dfs(f[node][i]));//求结点孩子的最深层数
	}
	return count + f[node].size();//递归出口,count为node结点孩子的最大孩子数目,f[node].size()是node这一层兄弟数目
}
int main() {
	int n;//n为结点个数
	int t;//
	cin >> n;
	for (int i = 2; i <= n; i++) {
		cin >> t;
		f[t].push_back(i);// i结点的父亲是x结点  所以利用f[t].size()可以求得t结点的孩子数目
	}
	cout << dfs(1);
	return 0;
}

C.砝码称重

大意:
给定n个砝码的重量,计算出可以称出多少种不同的重量。

思路:
背包
dp[i][j]表示前i个砝码能否称出j的重量,1为能,0为不能。
3种转移
1:dp[i-1][j],前i个砝码能称出j的重量。
2:dp[i-1][j+w[i]],前i个砝码能称出j+w[i]的重量,则把当前砝码放在物品那一边,即可称出当前重量。
3:dp[i-1][j-w[i]],前i个砝码能称出j-w[i]的重量,则把当前砝码放在砝码那一边,即可称出当前重量。

代码:

#include <iostream>
#include <math.h>
using namespace std;
int dp[101][100001] = {0};
int n,sum,ans;	
 
int main()
{
	cin >> n;
	int w[n+1];
	for(int i = 1;i <= n;i++)
	{
		cin >> w[i];
		sum += w[i]; // 总重
	}
	dp[0][0] = 1; // 初始状态
	for(int i = 1;i <= n;i++)
	{
		for(int j = 0;j <= sum;j++)
		{
			dp[i][j] = max(dp[i-1][j],max(dp[i-1][j+w[i]],dp[i-1][abs(j-w[i])]));
		}
	}
	for(int i = 1;i <= sum;i++)
	{
		if(dp[n][i])
		{
			ans++;
		}
	}
	cout << ans;
	return 0;
}

第二天

补题

F.阿宁的二进制

大意:
给定一个数组,和k次操作,每次操作选择一个数换成该数在二进制下1的个数。
问k次操作后最大的数最小值是多少。

思路:
对数进行操作就是将数变小,1除外。
每次取最大的数进行操作。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 3 + 2e5;
int n, q;
int a[N], b[N * 5], t;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; ++i) {
        while (a[i] > 1) {
            // 不用存1
            b[++t] = a[i];
            // __builtin_popcount就是求二进制中1的个数
            a[i] = __builtin_popcount(a[i]);
        }
    }
    sort(b + 1, b + t + 1);
    while (q--) {
        int k;
        cin >> k;
        if (k >= t) {
            cout << 1 << endl;
        } else {
            // 升序排序,b[t-k]就是第k+1大
            cout << b[t - k] << endl;
        }
    }
    return 0;
}

K.阿宁大战小红

大意:
给定一个正整数n,两个操作,+1或除以2向下取整。
当前数不能为0或曾出现过的数,否则输,问谁胜。

思路:
dfs搜索,
博弈必胜态必败态,
若当前数变成2n,则平局(一直+1操作)
当前数x,曾经最小y,最大z,
当前这个人p除以2下取整,p一定能赢,否则不如选+1.
当x+1<z,进行+1操作,
当x除以2向下取整<y,并且>0,可进行除2操作。

代码:

#include <bits/stdc++.h>
using namespace std;
map<int, bool> mp;
int n;
int get(int x, int y, int z) {
    return x * 1000000 + y * 1000 + z;
}

// (x,y,z)
// true 表示 (x,y,z) 必胜
// false 表示 (x,y,z) 必败
bool dfs(int x, int y, int z) {
    // 将(x,y,z)映射到一个正数
    int id = get(x, y, z);

    if (mp.count(id)) {
        // 记忆化搜索
        // 如果搜索过了,就不要再搜下去
        return mp[id];
    }
    // 没搜索过
    // 在mp创建一个bool
    // ans是mp[id]的一个引用,默认false
    bool& ans = mp[id];
    if (x + 1 < z && !dfs(x + 1, y, z)) {
        // (x + 1, y, z)是必败态
        // 那么(x, y, z)就是必胜态
        return ans = true;
    }
    if (x / 2 && x / 2 < y && !dfs(x / 2, x / 2, y)) {
        // (x/2, x/2, y)是必败态
        // 那么(x, y, z)就是必胜态
        return ans = true;
    }
    // 不能通过两种操作得到必败态
    // 那么(x,y,z)就是必败态
    return ans;
}
int main() {
    cin >> n;
    for (int i = n; i < 2 * n; ++i) {
        // 除以2后,当前值是i/2最小值是i/2,最大值是初始值n
        if (i / 2 && !dfs(i / 2, i / 2, n)) {
            if ((i - n) % 2 == 0) {
                cout << "ning" << endl;
            } else {
                cout << "red" << endl;
            }
            return 0;
        }
    }
    cout << "draw" << endl;
    return 0;
}

外卖店优先级

大意:
n家外卖店初始优先级为0,每过一个时间单位没有订单优先级-1,最小为0,有订单+2.
某时刻优先级>5,加入优先缓存,<=3,清除出缓存。
问T时刻有几家店在优先缓存中。

思路:
对订单按分店铺按时间先后排序,
记录下上次订单的时间,
用当前时间-间隔

代码:


#include <iostream>#include <algorithm>
using namespace std;

//顺带复习一下结构体的排序hhhint m,n,t;//store[id]第id号店上一次有的订单//flag[id]存储第id号店在不在优先缓存中//value[id]存储第id号店的得分

int store[100010],flag[100010],value[100010];

struct node{
    int time,id;}a[100010];

bool cmp(node a , node b){
    if(a.id==b.id)
        return a.time<b.time;
    return a.id<b.id;}

int main(){
    cin>>n>>m>>t;
    for(int i=0;i<m;i++){
        cin>>a[i].time>>a[i].id;
    }
    sort(a,a+m,cmp);
    for(int i=0;i<m;i++){
        int tt=a[i].time,id=a[i].id;
        //如果当前的订单不等于上一次的订单,则减去它们之间的间隔
        //因为如果没有订单,则每个时刻减1
        if(tt!=store[id])
            value[id]-=tt-store[id]-1;
        //value的值要大于等于0
        value[id]=max(0,value[id]);
        if(value[id]<=3)
            flag[id]=0;
        value[id]+=2;
        if(value[id]>5)
            flag[id]=1;
        store[id]=tt;
    }
    for(int i=1;i<=n;i++){
    //如果这个店的最后一个订单不是第 t 分钟的
    //就减去最后一个订单的时间到 t 时间之间应该减去的
        if(store[i]<t){
            value[i]-=t-store[i];
            if(value[i]<=3)
                flag[i]=0;
        }
    }
    int ans=0;
    for(int i=0;i<=n;i++)
        if(flag[i])
            ans++;
    cout<<ans;
    return 0;}

修改数组

大意:
大小为n的数组,从下标2开始,若元素与之前的元素相同,则+1。问最后的数组。

思路:
vis[]数组记录该数是否出现过,
并查集,对相邻数判断,进行压缩,提高搜索效率。

代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXA 1100005
int a,N,fa[MAXA],vis[MAXA];
int get_father(int x) //查找祖先
{
    if(x==fa[x]) return x;
    return fa[x] = get_father(fa[x]);
}
int main()
{
    cin>>N;
    for(int i=1;i<MAXA;i++) fa[i] = i; //并查集初始化
    for(int i=0;i<N;i++)
    {
        cin>>a;
        int ans=vis[a]?get_father(a)+1:a; //判断是否已被访问,已访问的话占取下一个位置
        cout<<ans<<' '; //输出ans
        vis[ans] = 1; //ans为已访问
        if(ans!=1&&vis[ans-1]) fa[ans-1] = ans; //表示ans-1的父亲是ans
        if(vis[ans+1]) fa[ans] = ans+1; //表示ans的父亲是ans+1
    }
    return 0;
}

第三天

cf

H.Perfect Array

大意:
对空数组进行以下操作:
第i个操作选择任意一个在1-i的整数并把它插入到第i个位置上。
给定一个数组判断它是否由以上操作得到,若是,输出选择数的原顺序。

思路:
逆向思维,
最后插入的一定是数值与位置对应的,
数组下标从0开始,故从右往左遍历a[i]==i+1的,加入ans数组,最后倒序输出。
从左往右会影响后面数的对应,故从右往左。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e5+5;
vector<int> v;
vector<int> ans;
int main()
{
    int n;cin>>n;
    for(int i=0;i<n;i++){
        int t;cin>>t;
        v.push_back(t);
    }
    bool g= false;
    while(v.size()) {
        bool f= false;
        for (int i = v.size() - 1; i >= 0; i--) {
            if (v[i] == i + 1) {
                ans.push_back(v[i]);
                v.erase(v.begin() + i);
                f= true;
                break;
            }
        }
        if(!f){
            g= true;
            break;
        }
    }
    if(g){
        cout<<"NO";
        return 0;
    }
    cout<<"YES"<<endl;
    for(int i=ans.size()-1;i>=0;i--){
        cout<<ans[i];
        if(i!=0)cout<<' ';
    }
    return 0;
}

第四天

二分和三分

二分模板(单调函数)

int main()
{
int num[9] = {1,2,3,3,4,4,5,5,7};//测试数据
int L = 0,R = 8;
int target = 5;

//求小于等于target的最后一个数的下标
while(L < R)
{
int mid = L + R + 1 >> 1;
if(num[mid] <= target) L = mid;
else R = mid - 1;
}

//求大于等于target的第一个数的下标
while(L < R)
{
int mid = L + R >> 1;
if(num[mid] >= target) R = mid ;
else L = mid + 1;
}

二分STL

lower_boudnd //返回第一个大于等于value的迭代器

upper_bound //返回第一个大于value的迭代器

binary_search //如果存在value,返回true,否则返回false

三分模板(单峰函数)

int sf(int l,int r){   
while(l<r-1){        
int mid=l+r>>1;        
int mmid=mid+r>>1;        if(f(mid)>f(mmid))r=mmid;        else l=mid;  
}
return f(l)>f(r)?l:r;}


第五天

cf

C.KK算日期

大意:
判断闰年,无公元0年,公元前年份取绝对值

思路:
无公元0年,
公元前年份--再判断

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=3e4+10;
priority_queue<ll,vector<ll>,greater<ll>> xz;
priority_queue<ll,vector<ll>,less<ll>> df;
priority_queue<ll,vector<ll>,greater<ll>> xf;
priority_queue<ll,vector<ll>,less<ll>> dz;

int main()
{
    ll n;scanf("%lld",&n);
    ll a;
    int cntf=0;
    for(ll i=1;i<=n;i++){
        scanf("%lld",&a);
        if(a>0){
            xz.push(a);
            dz.push(a);
        }
        else {
            df.push(a);
            xf.push(a);
            cntf++;
        }
    }
    if(cntf==0||cntf%2==0) {
        if(xz.size())
            printf("%lld",xz.top());
        else {
            printf("%lld",xf.top());
        }
    }
    else  {

        printf("%lld",df.top());

    }

    return 0;
}

第六天

错排问题

错排公式
f [ 0 ] = 1 , f [ 1 ] = 0 , f [ 2 ] = 1
f[ n ] = ( n − 1 ) ∗ ( f [ n − 1 ] + f [ n − 2 ] )
全错的概率 = 全错数 / 全部情况。
全部情况就是N的阶乘。
全错数:
1.将第N个数放在k位置,有n-1种。
2.将第k个位置的数拿出来考虑,如果第k个数放在第N个位置,则剩下就是n-2个数全部排错情况;
如果第k个数不是放在第N个位置,则就是n-1个数全部排错情况。

计算多边形面积

多边形的面积公式为s=1/2[(x1y2-x2y1)+(x2y3-x3y2)+...... +(XkYk+1-Xk+1Yk)+...+(Xny1-x1*Yn) ]

posted @ 2023-02-12 17:52  WW爆米花  阅读(16)  评论(0编辑  收藏  举报