【第四届】【省赛】【B组】

错误票据

   某涉密单位下发了某种票据,并要在年终全部收回。

  每张票据有唯一的ID号。全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。

  因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。

  你的任务是通过编程,找出断号的ID和重号的ID。

  假设断号不可能发生在最大和最小号。

  要求程序首先输入一个整数N(N<100)表示后面数据行数。
  接着读入N行数据。
  每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000)
  每个整数代表一个ID号。

  要求程序输出1行,含两个整数m n,用空格分隔。
  其中,m表示断号ID,n表示重号ID

  例如:
  用户输入:
  2
  5 6 8 11 9
  10 12 9

  则程序输出:
  7 9


  再例如:
  用户输入:
  6
  164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196
  172 189 127 107 112 192 103 131 133 169 158
  128 102 110 148 139 157 140 195 197
  185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190
  149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188
  113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119

  则程序输出:
  105 120

提取关键词

有唯一的ID号
ID号是连续的
ID的开始数码是随机选定的
某个ID断号,另外一个ID重号
断号不可能发生在最大和最小号
找出断号的ID和重号的ID
每个整数代表一个ID号

转化关键词

ID号两两不同
ID号逐渐递增或递减
ID号开头的数是随机的
断号:一个ID号的下一个ID不存在,重号:一个ID号重复出现
断号和重号不在开头或结尾出现

用关键词复述题意

给你一串数字,每个数字都是1个ID,现在有个ID号重复出现了两次,请你找出这个重复出现的ID号和这串数字中两两相差大于1的数字

思路

排序后,断号和重号发生在两个元素之间;若相邻两项之差大于1,则发生断号;若相邻两项相等,发生重号

时间复杂度

O(log(N) + N)

import java.util.Arrays;
import java.util.Scanner;

class MC{

    int N = (int) (1e5 + 10);
    int[] a = new int[N];
    int k = 0;

    public void run(){
        int n;
        n = sc.nextInt();
        //消耗换行符
        sc.nextLine();
        while (n -- > 0){
            for (String c : sc.nextLine().split(" ")) {
                if (c.length() == 0) continue;
                a[k++] = Integer.parseInt(c);
            }
        }
        //排序
        Arrays.sort(a, 0, k);
        int r1 = 0, r2 = 0;
        //遍历
        for (int i = 0; i < k - 1; i++) {
            //找出断号ID
            if (a[i + 1] - a[i] > 1) r1 = a[i] + 1;
            //找出重复ID
            if (a[i + 1] == a[i]) r2 = a[i];
        }
        System.out.printf("%d %d", r1, r2);
    }

    Scanner sc = new Scanner(System.in);
}

public class Main {
    public static void main(String[] args) {
        new MC().run();
    }
}

翻硬币

     小明正在玩一个“翻硬币”的游戏。

  桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

  比如,可能情形是:**oo***oooo

  如果同时翻转左边的两个硬币,则变为:oooo***oooo

  现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
  我们约定:把翻动相邻的两个硬币叫做一步操作,那么要求:

  程序输入:
  两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000

  程序输出:
  一个整数,表示最小操作步数

  例如:
  用户输入:
  **********
  o****o****

  程序应该输出:
  5

  再例如:
  用户输入:
  *o**o***o***
  *o***o**o***

  程序应该输出:
  1

提取关键词

* 表示正面,用 o 表示反面
翻动相邻的两个硬币叫做一步操作
两行等长的字符串
最少要翻动多少次

转化关键词

1为正面,0为反面
每次翻转要么和左边翻转,要么和右边翻转
字符串长度相等
求最小操作次数

用关键词复述题意

给定两个01序列A,B,每次操作把自身和相邻的左边或右边的数置反,问最小操作多少次,序列A与B相同

思路

看起来这是最短编辑距离的题,要用dp来解决。但dp要求无后效性,所以用不了dp,个人倾向这是道贪心题,同时遍历A与B,若A[i]与B[i]不同,则操作一次,把A[i]变成B[i]

时间复杂度

O ( N ) O(N) O(N)

import java.util.Scanner;
class MC{
    int N = 1010;
    int[] A = new int[N], B = new int[N];
    int[] f = new int[N];
    int n;
    public void run(){
        String s = sc.nextLine();
        n = s.length();
        for (int i = 0; i < n; i++) {
            if (s.charAt(i) == '*') A[i + 1] = 1;
        }
        s = sc.nextLine();
        for (int i = 0; i < n; i++) {
            if (s.charAt(i) == '*') B[i + 1] = 1;
        }
		
        for (int i = 1; i <= n; i++) {
            f[i] = f[i-1];
            if(A[i] != B[i]){
            	//1变0,0变1
                A[i] = 1 - A[i];
                A[i + 1] = 1 - A[i + 1];
                f[i] = f[i - 1] + 1;
            }
        }
        System.out.println(f[n]);
    }

    Scanner sc = new Scanner(System.in);
}

public class Main {
    public static void main(String[] args) {
        new MC().run();
    }
}

带分数

	100 可以表示为带分数的形式:100 = 3 + 69258 / 714

  还可以表示为:100 = 82 + 3546 / 197

  注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。

  类似这样的带分数,100 有 11 种表示法。

  题目要求:
  从标准输入读入一个正整数N (N<1000*1000)
  程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
  注意:不要求输出每个表示,只统计有多少表示法!


  例如:
  用户输入:
  100
  程序输出:
  11

  再例如:
  用户输入:
  105
  程序输出:
  6

提取关键词

数字1~9分别出现且只出现一次(不包含0)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数

转化关键词

用ABCDEFGHI表示带分数,则A-I都不相同且A-I都属于1-9

用关键词复述题意

给你1个数Z,这个数可以表示成多少种整数S+分数P的形式

思路

全排列+check()
在check里面,需要注意S的大小一定小于Z,等于也不行,因为不能出现0;又因为,Z是整数,S是整数,所以P也一定是整数,因此,P的分母一定能整除分子

时间复杂度

O ( 9 ! ) O(9!) O(9!)

import java.util.Scanner;

class MC{

    int N = 10;
    int n = 9;
    int[] a = new int[N];
    boolean[] st = new boolean[N];
    //zl:z的长度,cnt:结果
    int z, zl, cnt = 0;

    //把a种的l,r转为一个数字
    int toInt(int l, int r){
        int res = 0;
        while (l <= r){
            res = res * 10 + a[l];
            l++;
        }
        return  res;
    }

    void check(){
        //枚举加号位置
        for (int i = 1; i <= zl && i <= 7; i++) {
            int s = toInt(1, i);
            //枚举除号位置
            for (int j = i + 1; j <= 8; j++) {
                //p = p1/p2
                int p1 = toInt(i + 1, j);
                int p2 = toInt(j + 1, 9);
                //p1必须大于等于p2
                if (p1 < p2) continue;
                if (p1 % p2 != 0) continue;
                if (s + (p1 / p2) == z) cnt++;
            }
        }
    }


    void dfs(int cnt){
        if (cnt == n + 1){
            check();
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (st[i])continue;
            st[i] = true;
            a[cnt] = i;
            dfs(cnt + 1);
            st[i] = false;
        }
    }

    public void run(){
        z = sc.nextInt();
        int t = z;
        while (t > 0){
            zl ++;
            t /= 10;
        }
        dfs(1);
        System.out.println(cnt);
    }

    Scanner sc = new Scanner(System.in);
}

public class Main {
    public static void main(String[] args) {
        new MC().run();
    }
}

连号区间数

小明这些天一直在思考这样一个奇怪而有趣的问题:

  在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:

  如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。

  当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
输入格式
  第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
  第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。
输出格式
  输出一个整数,表示不同连号区间的数目。

  示例:
  用户输入:
  4
  3 2 4 1

  程序应输出:
  7

  用户输入:
  5
  3 4 2 5 1

  程序应输出:
  9

  解释:
  第一个用例中,有7个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [2,2], [3,3], [4,4]
  第二个用例中,有9个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [1,5], [2,2], [3,3], [4,4], [5,5]

提取

连号区间:区间[L, R] 里的所有元素递增排序后可以得到“连续”的数列
全排列的规模
这N个数字的某一全排列

转化

若区间[L,R]排序后,前后元素相差为1,则这个区间为连号区间
序列的长度
序列本身

复述

给你序列的长度和该序列,求该序列有多少个连号区间

思路

枚举
确定区间只需要区间左端点和区间长度,区间长度最低为0;得到该区间后排序判断前后相差是否为1即可

时间复杂度

O ( N ∗ N / 2 ) O(N*N/2) O(NN/2)

import java.util.Arrays;
import java.util.Scanner;

class MC{

    int N = (int) (5e4 + 10);
    int[] a = new int[N], cp = new int[N];
    int k = 0;
    int res = 0;

    void cpArr(int l, int r){
        for (int i = l; i <= r; i++) {
            cp[i] = a[i];
        }
    }

    public void run(){
        k = sc.nextInt();
        for (int i = 1; i <= k; i++) {
            a[i] = sc.nextInt();
        }
        //枚举区间左端点
        for (int i = 1; i <= k; i++) {
            //枚举区间长度
            for (int len = 0; len + i <= k; len++) {
                cpArr(i, i + len);
                //排序
                Arrays.sort(cp, i, i + len + 1);
                //判断前后元素是否相差1
                boolean flag = true;
                for (int j = i; j <= i + len - 1; j++) {
                    if (cp[j + 1] - cp[j] != 1){
                        flag = false;
                        break;
                    }
                }
                if (flag) res++;
            }
        }
        System.out.println(res);
    }

    Scanner sc = new Scanner(System.in);
}

public class Main {
    public static void main(String[] args) {
        new MC().run();
    }
}

幸运数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
错误票据C++源文件.zip 标题:错误票据 某涉密单位下发了某种票据,并要在年终全部收回。 每张票据有唯一的ID号。全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。 因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。 你的任务是通过编程,找出断号的ID和重号的ID。 假设断号不可能发生在最大和最小号。 要求程序首先输入一个整数N(N<100)表示后面数据行数。 接着读入N行数据。 每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000) 每个整数代表一个ID号。 要求程序输出1行,含两个整数m n,用空格分隔。 其中,m表示断号ID,n表示重号ID 例如: 用户输入: 2 5 6 8 11 9 10 12 9 则程序输出: 7 9 再例如: 用户输入: 6 164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196 172 189 127 107 112 192 103 131 133 169 158 128 102 110 148 139 157 140 195 197 185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190 149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188 113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119 则程序输出: 105 120 资源约定: 峰值内存消耗 < 64M CPU消耗 < 1000ms 请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。 所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。 注意: main函数需要返回0 注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。 注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而略常用头文件。 提交时,注意选择所期望的编译器类型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值