【比赛】算法设计与编程挑战赛——每日一题

很多题目都是洛谷上有的,可以自己去搜索
【参考:题目列表 - 洛谷 | 计算机科学教育新生态

在每日一题中,我会给出题目的算法标签以供同学们自学。 每日一题的难度会越来越大,会尝试覆盖尽可能多的算法竞赛高频考点。给出的题目均在vj.openjudge.com可以找到,对应的视频题解可以在往年真题或者基础班课程中看到(收费)。

每日一题

1.3.2 自然数拆分问题

【参考:P2404 自然数的拆分问题 - 洛谷 | 计算机科学教育新生态
【参考:SaikrVj | 1.3.2 自然数拆分问题
1月18日
题目标签:深度优先搜索入门

回溯法=DFS
【参考:题解 P2404 【自然数的拆分问题】 - beelake 的博客 - 洛谷博客

import java.io.*;
import java.util.*;

public class Main {
    static int n;
    static List<Integer> list = new ArrayList<>();

    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(System.out);

    public static void main(String[] args) throws IOException {
        String s1 = in.readLine();
        n = Integer.parseInt(s1);

        dfs(0, 1);

        out.flush();
    }

    // 7=1+1+1+1+1+1+1 所以不需要去重
    // 组合问题 无关顺序 这里需要去重
    // pre 上一个数
    public static void dfs(int sum, int start) {
        if (sum > n) return;

        if (sum == n) {
            out.print(list.get(0));
            for (int i = 1; i < list.size() - 1; i++) {
                out.print("+" + list.get(i));
            }
            out.println("+" + list.get(list.size() - 1));
            out.flush();
            return;
        }
        // 7 = 1 + 6 所以 i<=n-1
        for (int i = start; i <= n-1; i++) {
            // 做选择
            sum += i;
            list.add(i);

            dfs(sum, i);// 递归回溯

            // 撤销选择
            sum -= i;
            list.remove(list.size() - 1);
        }
    }
}

暴力枚举

#include<iostream>
#include<cstdio>
using namespace std;
int n;
int main()
{
	cin>>n;
	if(n==1)printf("\n");
		else if(n==2)printf("1+1\n");
			else if(n==3)printf("1+1+1\n1+2\n");
				else if(n==4)printf("1+1+1+1\n1+1+2\n1+3\n2+2\n");
					else if(n==5)printf("1+1+1+1+1\n1+1+1+2\n1+1+3\n1+2+2\n1+4\n2+3\n");
						else if(n==6)printf("1+1+1+1+1+1\n1+1+1+1+2\n1+1+1+3\n1+1+2+2\n1+1+4\n1+2+3\n1+5\n2+2+2\n2+4\n3+3\n");
							else if(n==7)printf("1+1+1+1+1+1+1\n1+1+1+1+1+2\n1+1+1+1+3\n1+1+1+2+2\n1+1+1+4\n1+1+2+3\n1+1+5\n1+2+2+2\n1+2+4\n1+3+3\n1+6\n2+2+3\n2+5\n3+4\n");
								else printf("1+1+1+1+1+1+1+1\n1+1+1+1+1+1+2\n1+1+1+1+1+3\n1+1+1+1+2+2\n1+1+1+1+4\n1+1+1+2+3\n1+1+1+5\n1+1+2+2+2\n1+1+2+4\n1+1+3+3\n1+1+6\n1+2+2+3\n1+2+5\n1+3+4\n1+7\n2+2+2+2\n2+2+4\n2+3+3\n2+6\n3+5\n4+4\n");
	return 0;
}

1.4.3 网线主管

【参考:SaikrVj | 1.4.3 网线主管
1月20日
题目标签:二分答案。
提示:对于可以使用枚举找出答案的题目,可以思考答案是否具有单调性,对于具有单调性的数据,我们即可使用二分法求解。

你需要编写一个程序,帮助网线主管确定一个最长的网线长度,并且按此长度对库存中的网线进行切割,能够得到指定数量的网线。

求最大值

与 【参考:P2440 木材加工 - 洛谷 | 计算机科学教育新生态】一样的题目

注意把浮点数化为整数再求

import java.io.*;
import java.util.*;

public class Main {
    static int n;// 库存中的网线数
    static int k;// 需要的网线数量
    static List<Integer> list = new ArrayList<>();

    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(System.out);

    public static void main(String[] args) throws IOException {
        String s1 = in.readLine();
        String[] arr1 = s1.split(" ");
        n = Integer.parseInt(arr1[0]);
        k = Integer.parseInt(arr1[1]);

        int[] arr = new int[n];
        int low = 1;// cm 最短为1cm
        // int high = 10000000; // 100KM -> cm
        int high = Integer.MIN_VALUE; // 100KM -> cm 最长为最长的那根
        double temp;
        for (int i = 0; i < n; i++) {
            temp = Double.parseDouble(in.readLine()); // m
            arr[i] = (int) (temp * 100); // cm
            high = Math.max(high, arr[i]);
        }
        int mid;
        int result = 0;
        while (low <= high) {
            mid = (low + high) >> 1;
            if (check(mid, arr)) {
                result = mid;
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        if (result == 0) {
            out.println("0.00");
        } else{
            out.printf("%.2f",result * 1.0 / 100); // (单位:米)。必须精确到厘米,即保留到小数点后两位。
        }

        out.flush();
    }

    public static boolean check(int mid, int[] arr) {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i] / mid;
        }

        return sum >= k;
    }
}

1.4.7 借教室

【参考:SaikrVj | 1.4.7 借教室
1月21日

题目标签:二分答案+差分数组
提示:仔细完成前两天的每日一题,掌握二分答案与差分数组,再来挑战今天的题目。

每张订单其实就可以看作是一个区间(操作),左右区间分别为开始时间和结束时间,所以这不就是一个区间操作吗 =》 差分数组

有上下界,序列满足单调性
如果订单号mid不能满足,此时mid为候选答案,如果[0,mid]里面可能有比mid还早的订单不能满足,那么继续向左靠直到找到最先不能满足的那个订单号 =》二分答案

题目:按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配

如果一个人无法被满足,则他后面的人全都不能被满足;如果一个人可以被满足,则他前面的人都可以被满足。这恰恰吻合了我们二分的性质。

【参考:题解 P1083 【借教室】 - ShawnZhou 的博客 - 洛谷博客

import java.io.*;
import java.util.*;

public class Main {
    static int n;
    static int m;
    static int[] line;// 第i天可用于租借的教室数量
    static long[] need;// 第i天需要租借的教室数量
    // static int[] s;
    // static int[] t;
    // static int[] d;
    static long[] change;//差分数组


    public static void main(String[] args) throws IOException {
        // BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(System.out);
        StreamTokenizer sc = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        sc.nextToken();
        n = (int) sc.nval;
        sc.nextToken();
        m = (int) sc.nval;

        // String s1 = in.readLine();
        // String[] arr1 = s1.split(" ");
        // n = Integer.parseInt(arr1[0]);
        // m = Integer.parseInt(arr1[1]);


        line = new int[n + 1];
        need = new long[n + 2];
        change = new long[n + 2];
        // 天数与订单均用从 1 开始的整数编号 ,所以这里也从下标1开始存储数据
        int[] d = new int[m + 1];
        int[] s = new int[m + 1];
        int[] t = new int[m + 1];


        // String s2 = in.readLine();
        // String[] arr2 = s2.split(" ");
        // for (int i = 0; i < n; i++) {
        //     line[i + 1] = Integer.parseInt(arr2[i]);
        // }

        for (int i = 0; i < n; i++) {
            sc.nextToken();
            line[i + 1] = (int) sc.nval;
        }

        // String s3;
        // String[] arr3;
        // for (int i = 0; i < m; i++) {
        //     s3 = in.readLine().trim();
        //     arr3 = s3.split(" ");
        //     d[i + 1] = Integer.parseInt(arr3[0]);
        //     s[i + 1] = Integer.parseInt(arr3[1]);
        //     t[i + 1] = Integer.parseInt(arr3[2]);
        // }

        for (int i = 0; i < m; i++) {
            sc.nextToken();
            d[i + 1] = (int) sc.nval;
            sc.nextToken();
            s[i + 1] = (int) sc.nval;
            sc.nextToken();
            t[i + 1] = (int) sc.nval;
        }

        //obj 4什么情况是全都可以成立
        // 检查前m个订单是否满足
        if (check(m, d, s, t)) {
            out.println(0);
            out.flush();
            return;
        }

        int low = 1;
        int high = m; // 最大的订单号

        // 二分答案最小值问题
        int mid;
        int result = 0;
        while (low <= high) {
            mid = (low + high) >> 1;
            if (check(mid, d, s, t)) {
                low = mid + 1;
            } else {
                result = mid;
                high = mid - 1;
            }
        }

        out.println(-1);
        out.println(result);
        out.flush();
    }

    // need:[1,mid]之间的需要房间总数,即前x个订单,所以每次都要置0
    // 判断能不能满足前x个订单
    public static boolean check(int x, int[] d, int[] s, int[] t) {
        Arrays.fill(need, 0);// 每次都要置0
        Arrays.fill(change, 0);// 每次都要置0
        // 因为need都为0,所以change初始化也全为0 change[i]=need[i]-need[i-1]

        // // 差分数组变化
        //obj 1用差分数组对前x个订单操作进行处理
        for (int i = 1; i <= x; i++) {
            change[s[i]] += d[i]; // 这里题目最大给了10^9 3个10^9相加就越界了,所以要用int
            change[t[i] + 1] -= d[i];
        }

        // 复原序列need
        //obj 2抹平差分数组
        for (int i = 1; i <= n; i++) {
            need[i] = need[i - 1] + change[i];
            if (need[i] > line[i]) // 无法满足
                return false;
        }


        // //obj 3 与上面合并到一起
        // for (int i = 1; i <= n; i++)
        //     if (need[i] > line[i]) // 无法满足
        //         return false;
        return true;
    }


}

1.6.5 算24 ?

【参考:SaikrVj | 1.6.5 算24

1月22日

题目标签:搜索+剪枝、模拟

【参考:P1236 算24点 - 洛谷 | 计算机科学教育新生态

1.6.7 移动玩具 ?

【参考:SaikrVj | 1.6.7 移动玩具
1月23日
题目标签:宽度优先搜索

【参考:P4289 [HAOI2008]移动玩具 - 洛谷 | 计算机科学教育新生态

1.6.6 奶酪 ?

【参考:SaikrVj | 1.6.6 奶酪
1月24日
题目标签:搜索、路径、数学

【参考:P3958 [NOIP2017 提高组] 奶酪 - 洛谷 | 计算机科学教育新生态

1.8.1 最短路计数 ?

【参考:SaikrVj | 1.8.1 最短路计数
1月25日
题目标签:最短路问题
【参考:P1144 最短路计数 - 洛谷 | 计算机科学教育新生态

1.8.2 最优贸易 ?

【参考:SaikrVj | 1.8.2 最优贸易
【1月26日每日一题】

题目标签:最短路问题 建立反向边
【参考:P1073 [NOIP2009 提高组] 最优贸易 - 洛谷 | 计算机科学教育新生态

1.8.3 Telephone Lines S ?

【参考:SaikrVj | 1.8.3 Telephone Lines S

【1月27日每日一题】

题目标签:最短路问题 分层图 分层图最短路

【参考:P1948 [USACO08JAN]Telephone Lines S - 洛谷 | 计算机科学教育新生态

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值