2024年2月27号题解

P1012 [NOIP1998 提高组] 拼数

解题思路

  1. 要组成一个最大的整数,根据贪心的思想我们可以知道只要比较数的每一位选其中最大得就可以组成一个最大得数
  2. 但可能有些数的前几位相同,因此在选择的数的个数大于两个数时,选其中位数最短的
  3. 选择的数的个数等于两个数时选其中最长的

代码实现

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#define u unsigned
#define ll long long
#define sc scanf
#define pr printf 
#define fr(i, j, n) for (int i = j; i < n; i++)
#define N 21

struct l {
	int val;
	struct l* next;
};

typedef struct l l;

int n;//数组元素的个数
int cnt;//数组元素的个数
l* head;//虚拟头节点
int t;//用来暂时存储输入的数值

l* mn(void) {//制造一个链表节点
	l* ans = (l*)malloc(sizeof(l));

	ans->next = 0;

	return ans;
}

bool f(l* p, l* max) {//比较两个链表节点谁大,如果p大,返回true,max大返回false
	int c = max->val;
	int d = p->val;
	int mb = log10(c);//取出c的位数
	int pb = log10(d);//取出d的位数
	int f = pow(10, mb);//拿到对应的值,方便后续比较
	int g = pow(10, pb);//拿到对应的值,方便后续比较
	while (f && g) {//如果两个数还有位数没有,比较
		if (c / f % 10 < d / g % 10) {//如果max的对应的位数小于p的对应位数,那么p大,返回true
			return true;
		}
		else if (c / f % 10 == d / g % 10) {//如果max的对应的位数等于p的对应位数,那么继续比较
			f /= 10;
			g /= 10;
		}
		else {//如果max的对应的位数大于p的对应位数,那么max大,返回false
			return false;
		}
	}

	if (cnt != 2) {//如果选择元素的个数不等于2
		if (!g) {//p是短的数,那么p就是大的数
			return true;
		}
		else {//p是长的数,那么p是小的数
			return false;
		}
	}
	else {//如果选择元素的个数等于2
		if (!g) {//p是短的数,那么p就是小的数
			return false;
		}
		else {//p是长的数,那么p就是大的数
			return true;
		}
	}
}

int main(int argc, char* argv[])
{
	sc("%d", &n);//数组元素的个数

	cnt = n;

	head = mn();//初始化

	//读入n个数到链表
	for (int i = 0; i < n; i++) {
		sc("%d", &t);
		l* temp = mn();//开辟一个节点存放输入的值

		//插入到链表里面
		temp->val = t;
		temp->next = head->next;
		head->next = temp;
	}

	while (cnt) {//只要还有可以选择的数
		l* max = head->next;//假设第一个节点的值是最大的
		l* pre1 = head;//指向max节点的前一个节点,用来删除max节点用
		l* pre2 = max;//指向p节点的前一个节点,用来给pre1赋值
		l* p = max->next;
		while (p) {//遍历整个链表
			if (f(p, max)) {//如果p的值大于max的值,那么更新max的值,和它的前驱指针
				pre1 = pre2;
				max = p;
			}
			pre2 = p;//先记住p之前的位置
			p = p->next;//p去下一个节点
		}

		pr("%d", max->val);//输出最大值
		pre1->next = max->next;//让max节点从链表中断开
		free(max);//释放max指针的空间
		cnt--;//因为删除了一个节点,所以选择元素的个数减少一个
	}

	return 0;
}

2583.二叉树中的第k大层和

解题思路

  1. 用层序遍历每一层节点,并算出每层的累加和,存入一个数组
  2. 给数组排序,最后返回数组元素减去k的位置的数

代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

typedef long long ELEMENT;
typedef struct TreeNode tr;

void quickSort(ELEMENT *a, int low, int high);
void swap(ELEMENT *a, int low, int high);//½»»»a[low]ºÍa[high]µÄÖµ 
int* partition(ELEMENT *a, int low, int high, ELEMENT key);//°ÑaÖÐlowµ½highµÄÊý°´ÕÕkey·ÖÀ࣬СÓÚkeyµÄÔÚ×ó±ß£¬µÈÓÚkeyµÄÔÚÖм䣬´óÓÚkeyµÄÔÚÓÒ±ß
 
long long kthLargestLevelSum(struct TreeNode* root, int k) {
    if (root == NULL) {//如果根节点为空,返回-1
        return -1;
    }

    tr* q[100001] = {0};//队列
    int l = 0;//队列头
    int r = 0;//队列尾
    long long ans[100001] = {0};//存放每一层的累加和数组
    int size = 0;//累加和数组的元素个数

    q[r ++] = root;//根节点入队
    ans[size ++] = root -> val;//第一层只有一个元素,所以直接放入累加和数组

    while(l < r) {//队列中还有元素
        int len = r - l;//取出每一层的节点个数
        long long sum = 0;//算每一层的累加和,要是long long不然int会溢出

        for (int i = 0; i < len; i ++) {//访问每一层节点
            tr* t = q[l++];//出队
            if (t -> left) {//节点有左孩子
                sum += t -> left -> val;//算累加和
                q[r ++] = t -> left;//左孩子入队
            }
            if (t -> right) {//节点有右孩子
                sum += t -> right -> val;//算累加和
                q[r ++] = t -> right;//右孩子入队
            }
        }
        ans[size ++] = sum;//把累加和放入累加和数组
    }

    quickSort(ans, 0, size - 1);//给数组按升序排序

    return size <= k? -1: ans[size - k];;//如果树少于等于 k 层,则返回 -1 ,否则返回数组元素减去k的位置的数
}

void quickSort(ELEMENT *a, int low, int high)
{
	int *p = NULL;//´æ´¢µÈÓÚ»ù×¼ÖµµÄ×óÓұ߽ç 
	
	if ( low >= high) {//Èç¹ûÖ»ÓÐÒ»¸öÖµ²»ÓÃÅÅÐò¾ÍÖ±½Ó½áÊøÅÅÐò 
		return ;
	}
	
	p = partition(a, low, high, a[low + rand() % (high - low + 1)]);//pÖ¸ÏòµÈÓÚ»ù×¼ÖµÇø¼ä×óÓұ߽ç 
	quickSort(a, low, p[0] - 1);//Ö»ÅÅСÓÚ»ù×¼ÖµµÄ²¿·Ö 
	quickSort(a, p[1] + 1, high);//Ö»ÅÅ´óÓÚ»ù×¼ÖµµÄ²¿·Ö
	free(p);//Êͷŵô¿ª±ÙµÄ¿Õ¼ä 
}
void swap(ELEMENT *a, int low, int high)
{
	ELEMENT t = 0;
	
	t = a[low];
	a[low] = a[high];
	a[high] = t;
}
int* partition(ELEMENT *a, int low, int high, ELEMENT key)
{
	int l = low;//´ú±íµÈÓÚkeyÇø¼äµÄ×ó±ß½ç,»òÕßÊÇСÓÚÇø¼äµÄÏÂÒ»¸öÊý  
	int r = high;//´ú±íµÈÓÚkeyÇø¼äµÄÓұ߽ç,»òÕßÊÇ´óÓÚÇø¼äµÄÏÂÒ»¸öÊý  
	int i = low;//´ÓÍ·¿ªÊ¼±éÀú 
	
	while(i <= r) {//ֻҪûÓе½´óÓÚÇø¼ä 
		if (a[i] < key) {//ϱêiµÄÊýСÓÚ»ù×¼Öµ£¬·ÅÔÚ×ó±ß½çÀïÃæ 
			swap(a, l++, i++);//°Ñ×ó±ß½çµÄºóÒ»¸öÊýºÍϱêiµÄÖµ½»»»£¬È»ºó×óÇø¼äÀ©ÕÅ£¬È»ºóÈ¥¿´ÏÂÒ»¸öÊý 
		}
		else  if (a[i] > key){//ϱêiµÄÊý´óÓÚ»ù×¼Öµ£¬·ÅÔÚÓұ߽çÀïÃæ 
			swap(a, r--, i);//°ÑÓұ߽çµÄǰһ¸öÊýºÍϱêiµÄÖµ×ö½»»»£¬È»ºóÓÒÇø¼äÀ©ÕÅ£¬µ«i²»Äܶ¯£¬ÒòΪµ±Ç°iµÄÖµ»¹Ã»ÓзÃÎʹý 
		}
		else {//ϱêiµÄÊýµÈÓÚ»ù×¼Öµ£¬·ÅÔÚ×óÓұ߽çÖ®¼ä 
			i++;//Ö±½Ó¼Ó1
		}
	} 
	int *p = (int *)malloc(sizeof(int ) * 2);//´æ·ÅµÈÓÚÇø¼äµÄ×óÓұ߽ç 
	p[0] = l;//µÈÓÚÇø¼ä×ó±ß½ç¾ÍÊÇl
	p[1] = r;//µÈÓÚÇø¼ä×ó±ß½ç¾ÍÊÇr
	return p;//·µ»ØµÈÓÚkeyÇø¼äµÄ×óÓұ߽ç 
}

2476.二叉搜索树最近节点查询

解题思路

  1. 因为题目没有说这个二叉搜索树是平衡的,所以它最坏情况可以退化成一个链表那么最坏的查询就成了O(n),n为二叉树的节点个数,因此会超时
  2. 所以我们可以利用二叉搜索树的性质即对二叉搜索树进行中序遍历得到的数组就是一个有序的数组,然后对这个有序数组的查询就是O(logN)。

代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
//把二叉搜索树进行中序遍历得到一个有序数组
void f3(struct TreeNode* root, int tree[], int* size) {
    if (root == NULL) {
        return ;
    }

    f3(root -> left, tree, size);
    tree[(*size)++] = root -> val;
    f3(root -> right, tree, size);
}

//返回有序数组tree中小于等于 target 的 最大值
int f1(int tree[], int target, int size) {
    int ans = -1;
    int l = 0;
    int r = size - 1;

    while(l <= r) {
        int mid = l + (r - l >> 1);

        if (tree[mid] <= target) {
            ans = tree[mid];
            l = mid + 1;
        }
        else {
            r = mid - 1;
        }
    }

    return ans;
}

//返回有序数组tree中大于等于 target 的 最小值
int f2(int tree[], int target, int size) {
    int ans = -1;
    int l = 0;
    int r = size - 1;

    while(l <= r) {
        int mid = l + (r - l >> 1);

        if (tree[mid] >= target) {
            ans = tree[mid];
            r = mid - 1;
        }
        else {
            l = mid + 1;
        }
    }

    return ans;
}

int** closestNodes(struct TreeNode* root, int* queries, int queriesSize, int* returnSize, int** returnColumnSizes) {
    int** ans = (int**)malloc(sizeof(int*) * queriesSize);//开辟一个二维动态数组
    int* len = (int*)malloc(sizeof(int) * queriesSize);//开辟一个每列的长度的数组用来记录每一列的长度
    int tree[100000] = {0};//用来存放二叉树中序遍历的数组
    int size = 0;//二叉树中序遍历的数组的元素个数

    for (int i = 0; i < queriesSize; i ++) {//为每一个指针开辟空间
        ans[i] = (int*)malloc(sizeof(int) * 2);
        len[i] = 2;//固定为2
    }

    f3(root, tree, &size);//得到中序遍历的数组

    for (int i = 0; i < queriesSize; i ++) {//遍历queries数组
        ans[i][0] = f1(tree, queries[i], size);//得到树中小于等于 queries[i] 的 最大值 
        ans[i][1] = f2(tree, queries[i], size);//得到树中大于等于 queries[i] 的 最小值
    }

    *returnSize = queriesSize;//二维数组的行数
    *returnColumnSizes = len;//二维数组每一列的列数

    return ans;//返回二维数组
}

938.二叉搜索树的范围和

解题思路

  1. 和上一道题差不多,因为题目没有说是平衡的,所以我们还是把二叉搜索树进行中序遍历得到一个升序的数组
  2. 找到大于等于left的最左位置,这样一定不会有位置不存在的情况,避免了-1下标
  3. 找到小于等于right的最右位置,这样也一定不会有位置不存在的情况,避免了-1下标

代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
//把二叉搜索树转换成一个升序数组
void f3(struct TreeNode* root, int tree[], int* size) {
    if (root == NULL) {
        return ;
    }

    f3(root -> left, tree, size);
    tree[(*size)++] = root -> val;
    f3(root -> right, tree, size);
}
//返回tree数组中大于等于target的最左位置
int f1(int tree[], int target, int size) {
    int ans = -1;
    int l = 0;
    int r = size - 1;

    while(l <= r) {
        int mid = l + (r - l >> 1);

        if (tree[mid] >= target) {
            ans = mid;
            r = mid - 1;
        }
        else {
            l = mid + 1;
        }
    }

    return ans;
}
//返回tree数组中小于等于target的最右位置
int f2(int tree[], int target, int size) {
    int ans = -1;
    int l = 0;
    int r = size - 1;

    while(l <= r) {
        int mid = l + (r - l >> 1);

        if (tree[mid] <= target) {
            ans = mid;
            l = mid + 1;
        }
        else {
            r = mid - 1;
        }
    }

    return ans;
}

int rangeSumBST(struct TreeNode* root, int low, int high) {
    int tree[20000] = {0};//二叉搜索树的中序遍历的数组
    int size = 0;//二叉搜索树的中序遍历的数组的元素个数
    int left = -1;//存放在中序遍历数组[low, high]左边界
    int right = -1;//存放在中序遍历数组[low, high]右边界
    long long ans = 0;//存放在中序遍历数组[low, high]中的所有节点的值得累加和,用long long防止溢出

    f3(root, tree, &size);//得到中序遍历得数组

    left = f1(tree, low, size);//得到左边界
    right = f2(tree, high, size);//得到右边界

    while(left <= right) {//只要没有越界
        ans += tree[left ++];//加上当前节点
    }

    return ans;//返回结果
}

P1192 台阶问题

解题思路

  1. 利用动态规划的思想可以得出每个位置的值等于从下标j = i - k到i - 1下标的累加和
  2. 但j不能小于0

代码实现

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#define u unsigned
#define ll long long
#define sc scanf
#define pr printf 
#define fr(i, j, n) for (int i = j; i < n; i++)
#define N 100001

int mod = 100003;//取模的数 
int ans[N];//每级台阶的方法数 
int n;//台阶的级数 
int k;//一次最多可以走几阶台阶 

int main(int argc, char* argv[])
{
	sc("%d%d", &n, &k);//读入n和k 
	
	ans[0] = ans[1] = 1;//初始化 
	
	for (int i = 2; i <= n; i ++) {//从台阶二开始算 
		for (int j = i - 1; j >= 0 && j >= i - k; j -- ) {//每级台阶的方法数等于从下标i - k到i - 1累加和 
			ans[i] += ans[j];//算累加和 
			ans[i] %= mod;//每次中间结果取模 
		}
	}
	
	pr("%d", ans[n]);//最后输出第n级台阶的方法数 
	
	return 0;
}

 

posted @ 2024-02-27 21:58  lwj1239  阅读(28)  评论(0)    收藏  举报