蓝桥杯算法提高
·
蓝桥杯算法提高
1.排列数
2.生物芯片
3.稍大的串
4.大臣的旅费
5.Excel地址转换
6.买不到的数
7.趣味算式
8.日程表
1. 排列数
时间限制:1.0s 内存限制:256.0MB
问题描述
0、1、2三个数字的全排列有六种,按照字母序排列如下:
012、021、102、120、201、210
输入一个数n
求0~9十个数的全排列中的第n个(第1个为0123456789)。
012、021、102、120、201、210
输入一个数n
求0~9十个数的全排列中的第n个(第1个为0123456789)。
输入格式
一行,包含一个整数n
输出格式
一行,包含一组10个数字的全排列
样例输入
1
样例输出
0123456789
数据规模和约定
0 < n <= 10!
深搜代码:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.Scanner;
public class Main {
static int arr[] = {0,1,2,3,4,5,6,7,8,9};
static int visited[] = {0,0,0,0,0,0,0,0,0,0};
static int n,k = 0;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
dfs(0);
}
public static void dfs(int offset) {
if(offset == 10 ) {
k++;
if(k == n) {
for(int i = 0; i <= 9; i++) {
System.out.print(arr[i]);
}
System.out.println();
return ;
}
}
for(int i = 0; i < 10; i++) {
if(visited[i] == 0) {
visited[i] = 1;
arr[offset] = i;
dfs(offset+1);
visited[i] = 0;
}
}
}
}
也可以用c++的全排函数:
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
string s = "0123456789";
int n;
cin >> n;
int cnt = 1;
do {
if(cnt == n) {
cout << s;
break;
}
cnt++;
}while(next_permutation(s.begin(), s.end()));
return 0;
}但我试着用自己学到的全排函数的模板来写,却不通过,应该顺序不一样。 import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.Scanner;
public class Main {
static int arr[] = {0,1,2,3,4,5,6,7,8,9};
static int n,k = 0;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
dp(0);
}
public static void dp(int offset) {
if(offset == 10 - 1) {
k++;
if(k == n) {
for(int i = 0; i <= 9; i++) {
System.out.print(arr[i]);
}
System.out.println();
return ;
}
}
for(int i = offset; i < 10; i++) {
swap(i, offset);
dp(offset + 1);
swap(i, offset);
}
}
public static void swap(int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
2.标题:生物芯片
X博士正在研究一种生物芯片,其逻辑密集度、容量都远远高于普通的半导体芯片。
博士在芯片中设计了 n 个微型光源,每个光源操作一次就会改变其状态,即:点亮转为关闭,或关闭转为点亮。
这些光源的编号从 1 到 n,开始的时候所有光源都是关闭的。
博士计划在芯片上执行如下动作:
所有编号为2的倍数的光源操作一次,也就是把 2 4 6 8 ... 等序号光源打开
所有编号为3的倍数的光源操作一次, 也就是对 3 6 9 ... 等序号光源操作,注意此时6号光源又关闭了。
所有编号为4的倍数的光源操作一次。
.....
直到编号为 n 的倍数的光源操作一次。
X博士想知道:经过这些操作后,某个区间中的哪些光源是点亮的。
【输入格式】
3个用空格分开的整数:N L R (L<R<N<10^15) N表示光源数,L表示区间的左边界,R表示区间的右边界。
【输出格式】
输出1个整数,表示经过所有操作后,[L,R] 区间中有多少个光源是点亮的。
例如:
输入:
5 2 3
程序应该输出:
2
再例如:
输入:
10 3 6
程序应该输出:
3
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
思路:仔细想一想就会发现在l-r的区间中,只有完全平方数的因子是奇数个,因为因数都是成对出现的,所以完全平方数的光源是暗的(加上自己本身因子为偶数个)。
编号:1 2 3 4 5 6 7 8 9
倍数:2 3 4 5 6 7 8 9
个数:0 1 1 2 1 3 1 3 2
所以代码如下:
#include"iostream"
#include"math.h"
using namespace std;
typedef long long ll;
int main()
{
ll n,l,r;
cin>>n>>l>>r;
ll beg=sqrt(l);
ll end=sqrt(r);
ll num=end-beg;
if(beg*beg==l) num++;
cout<<r-l+1-num<<endl;
return 0;
}3.标题:稍大的串
串可以按照字典序进行比较。例如:
abcd 小于 abdc
如果给定一个串,打乱组成它的字母,重新排列,可以得到许多不同的串,在这些不同的串中,有一个串刚好给定的串稍微大一些。科学地说:它是大于已知串的所有串中最小的串。你的任务就是求出这个“稍大的串”。
例如:
输入串:
abfxy
程序应该输出:
abfyx
再例如:
输入串:
ayyyxxff
程序应该输出:
fafxxyyy
数据规模约定:
输入的串不超过1000个字符。
特例:
如果已知的串已经是所有重组串中最大的,则原样输出读入的那个串。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
思路:
找到某个排列的下一个相邻排列
比本排列大的所有排列中,选字典序最小的
1. 逆向搜索一对相邻的元素,[x1,x2] 满足 [x1] < [x2], 若失败,则不存在下一个排列(本排列最大)
2. 逆向搜索一个元素 [y] 满足 [y]>[x1], 显然,最坏情况下,x2就是y,所以一定能找到
3. 交换: [x1] <--> [y]
4. [x2,...] x2以后的序列翻转
import java.util.*;
public class Pai
{
static boolean f_next(char[] a){
int x1 = -1;
for(int i=a.length-1; i>0; i--){
if(a[i-1]<a[i]){
x1 = i-1;
break;
}
}
if(x1<0) return false;
int x2 = x1 + 1;
int y = -1;
for(int i=a.length-1; i>0; i--){
if(a[i]>a[x1]){
y = i;
break;
}
}
{char t = a[x1]; a[x1] = a[y]; a[y] = t;}
y = a.length-1;
while(true){
if(x2>=y) break;
char t = a[x2];
a[x2] = a[y];
a[y] = t;
x2++;
y--;
}
return true;
}
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
char[] a = scan.nextLine().trim().toCharArray();
f_next(a);
for(int i=0; i<a.length; i++){
System.out.print(a[i]);
}
System.out.println();
}
}
4.标题:大臣的旅费
很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式:
输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数
城市从1开始依次编号,1号城市为首都。
接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)
每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。
输出格式:
输出一个整数,表示大臣J最多花费的路费是多少。
样例输入:
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出:
135
样例说明:
大臣J从城市4到城市5要花费135的路费。
资源约定:
峰值内存消耗 < 64M
CPU消耗 < 5000ms
思路:
这题给的时间有五秒那么多。
本题给出的一个图满足要求“使得任何一个大城市都能从首都直接或者通过其他大城市间接到达,同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的”,这说明给出的道路是一棵树。
使用传统的路径算法可以得到部分分,比如Floyd复杂度为O(n3)得分约30%,使用DFS或BFS复杂度为O(n2)得分约50%,
由于是一棵树,可以使用树中的最长路径的算法。从任意一点开始使用BFS找到最远点A,然后再从A开始BFS找到最远点B,AB即所求,复杂度为O(n),可以得到100%的分数。
最后的答案需要使用double或者long long类型计算。
import java.util.Arrays;
import java.util.Scanner;
public class Main {//单纯的用二维数组会超时,为什么用动态数组就不会内存了呢
public static int[][] Map = new int[7010][7010];
public static int[] vis = new int[7010];
public static int ans = -99999,temp,n;
static void dfs(int s,int dist){//深度优先搜索
//总的花费
if(dist > ans ){ //保存最长路以及最长路端点
ans = dist;
temp = s;//这里非常的关键,要进行最远路的点的更换
}
for(int i=1;i<=n;i++){
if(vis[i]==0 && Map[s][i]>0){
//如果没有被访问过 而且 s与i间的路程有效
vis[i] = 1;//被置为访问过
dist += Map[s][i];//累加路程
dfs(i,dist);//i 变 s 再进行下一
vis[i] = 0;
dist -= Map[s][i];
}
}
return;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i=0;i<n-1;i++){
int a = sc.nextInt();
int b = sc.nextInt();
int c = sc.nextInt();
Map[a][b] = Map[b][a] = c;
}
vis[1] = 1;//这个初始化非常的关键,你从第一个点开始搜,第一个点当然遍历到了
dfs(1,0);//搜索最长路起始点
Arrays.fill(vis, 0);//什么要还原一定要想明白,不要有遗漏知道吗
vis[temp] = 1;//从最远端再搜到另一个最远端
ans = -99999; //搜到的就是最长路
dfs(temp,0);
int sum = (11+10+ans)*ans/2;//x+10的线性费用//他妈的最后一步就忘了?傻逼,为了成功,必须要克服这些弱点,否则你的优点就会被埋没
System.out.println(sum);
}
}
c++版:#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 100010;
class Edge {
public:
int to;
int len;
Edge *next;
};
int n;
Edge* first[MAXN];
Edge edges[MAXN * 2];
int mlen;
bool reached[MAXN];
int q[MAXN];
int len[MAXN];
int getF(int x)
{
memset(reached, 0, sizeof(reached));
int open = 0, close = 0;
len[x] = 0;
reached[q[open++]=x] = true;
mlen = 0;
int ret = x;
while (close < open)
{
int p = q[close++];
for (Edge *e = first[p]; e; e = e->next)
{
if (!reached[e->to])
{
int t = len[e->to] = len[p] + e->len;
if (t > mlen)
{
mlen = t;
ret = e->to;
}
reached[q[open++]=e->to] = true;
}
}
}
return ret;
}
int main()
{
memset(first, 0, sizeof(first));
scanf("%d", &n);
Edge *cur = edges;
for (int i = 1; i < n; ++i)
{
int p, q, d;
scanf("%d%d%d", &p, &q, &d);
cur->to = q;
cur->len = d;
cur->next = first[p];
first[p] = cur;
++cur;
cur->to = p;
cur->len = d;
cur->next = first[q];
first[q] = cur;
++cur;
}
int a = getF(1);
int b = getF(a);
int ans = mlen;
double tu = ans;
double ta = tu * 10.0 + tu * (tu+1) / 2.0;
printf("%.0lf\n", ta);
return 0;
}
5.Excel地址转换
【编程题】
Excel是最常用的办公软件。每个单元格都有唯一的地址表示,比如:第12行第4列表示为:“D12”,第5行第255列表示为“IU5”。
事实上,Excel提供了两种地址表示方法,还有一种表示法叫做RC格式地址。 第12行第4列表示为:“R12C4”,第5行第255列表示为“R5C255”。
你的任务是:编写程序,实现从RC地址格式到常规地址格式的转换。
【输入、输出格式要求】
用户先输入一个整数n(n<100),表示接下来有n行输入数据。
接着输入的n行数据是RC格式的Excel单元格地址表示法。
程序则输出n行数据,每行是转换后的常规地址表示法。
例如:用户输入:
2
R12C4
R5C255
则程序应该输出:
D12
IU5
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main{
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
String result[] = new String[n];
int count = 0;
while (n > 0) {
String data = br.readLine();
int index_C = data.indexOf('C');
int x = Integer.parseInt(data.substring(1, index_C));
int y = Integer.parseInt(data.substring(index_C + 1, data.length()));
String address = "";
while (y > 26) {
int mo = y % 26;
y /= 26;
if (mo == 0) {
// 因为是整除,比如702(26*27)%26==0
// y = 702 / 26 = 27,那么可以把26*27看成26*(26+1)--> 26 * 26(Z) + 26(Z)
address = "Z" + address;
y -= 1;// 减去一个1,是因为已经拿出一个Z来表示了
} else
address = (char) (mo + 64) + address;
}
address = (char) (y + 64) + address + x;
result[count++] = address;
n--;
}
for (int i = 0; i < result.length; i++) {
System.out.println(result[i]);
}
}
} 6.标题:买不到的数目
小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
输入:
两个正整数,表示每种包装中糖的颗数(都不多于1000)
要求输出:
一个正整数,表示最大不能买到的糖数
例如:
用户输入:
4 7
程序应该输出:
17
再例如:
用户输入:
3 5
程序应该输出:
7
资源约定:
峰值内存消耗 < 64M
CPU消耗 < 3000ms
方法一:用数组记录值
import java.util.*;
public class Main
{
static final int N = 1000 * 100;
static int se(int[] da, int a)
{
int n=0;
for(int i=0; i<da.length; i++)
{
if(da[i]==1){
n++;
if(n>=a) return i-a;
}
else
n = 0;
}
return -1;
}
static void f(int a, int b)
{
int[] da = new int[N];
for(int i=0; i<=N/a; i++)
{
for(int j=0; j<=(N-i*a)/b; j++)
{
if(i*a + b*j < N) da[i*a+b*j] = 1;
}
}
System.out.println(se(da, a));
}
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
int num1 = scan.nextInt();
int num2 = scan.nextInt();
f(num1,num2);
}
}方法二:逆向判断该数是否能凑成
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int max = 0;
int num1 = 0, num2 = 0;
String strTemp = "";
Scanner scan = new Scanner(System.in);
strTemp = scan.nextLine();
String temp[] = strTemp.split(" ");
num1 = Integer.valueOf(temp[0]);
num2 = Integer.valueOf(temp[1]);
int maxNum = Math.max(num1, num2);
int minNum = Math.min(num1, num2);
int temNum1, temNum2;
max = maxNum * maxNum;
for (int i = max; i > maxNum; i--) {
temNum1 = i / maxNum;
if (i % maxNum != 0 && i % minNum != 0) {
for (int v = temNum1; v >= 1; v--) {
temNum2 = i - maxNum * v;
if (temNum2 % minNum != 0) {
temNum1--;
}
}
}
if (temNum1 == 0) {
System.out.println(i);
break;
}
}
}
} 7.匪警请拨110,即使手机欠费也可拨通!为了保障社会秩序,保护人民群众生命财产安全,警察叔叔需要与罪犯斗智斗勇,因而需要经常性地进行体力训练和智力训练!
某批警察叔叔正在进行智力训练:
1 2 3 4 5 6 7 8 9 = 110;
请看上边的算式,为了使等式成立,需要在数字间填入加号或者减号(可以不填,但不能填入其它符号)。之间没有填入符号的数字组合成一个数,例如:12+34+56+7-8+9 就是一种合格的填法;123+4+5+67-89 是另一个可能的答案。
请你利用计算机的优势,帮助警察叔叔快速找到所有答案。
每个答案占一行。形如:
12+34+56+7-8+9
123+4+5+67-89
......
已知的两个答案可以输出,但不计分。
各个答案的前后顺序不重要。
// 数字间填写符号 + -
// 1 2 3 4 5 6 7 8 9 = 110;
import java.util.*;
public class B33
{
public static void f(String cur, int goal, List<Integer> lst)
{
if(lst.size()==0) return;
int a = lst.remove(lst.size()-1);
if(lst.size()==0)
{
if(goal==a) System.out.println(a + cur);
return;
}
List<Integer> lst2 = new Vector<Integer>();
lst2.addAll(lst);
List<Integer> lst3 = new Vector<Integer>();
lst3.addAll(lst);
f("+" + a + "" + cur, goal-a, lst2);
f("-" + a + "" + cur, goal+a, lst3);
int b = lst.remove(lst.size()-1);
lst.add(Integer.parseInt(b+""+a));
f(cur, goal, lst);
}
public static void main(String[] args)
{
List<Integer> lst = new Vector<Integer>();
for(int i=1; i<=9; i++) lst.add(i);
f("", 110, lst);
}
}8.日程表
某保密单位机要人员 A,B,C,D,E 每周需要工作5天,休息两天。
上级要求每个人每周的工作日和休息日必须是固定的,不能在周间变更。
此外,由于工作需要,还有如下要求:
1. 所有人的连续工作日不能多于3天(注意:周日连到下周一也是连续)。
2. 一周中,至少有3天所有人都是上班的。
3. 任何一天,必须保证 A B C D 中至少有2人上班。
4. B D E 在周日那天必须休息。
5. A E 周三必须上班。
6. A C 一周中必须至少有4天能见面(即同时上班)。
你的任务是:编写程序,列出ABCDE所有可能的一周排班情况。工作日记为1,休息日记为0
A B C D E 每人占用1行记录,从星期一开始。
【输入、输出格式要求】
程序没有输入,要求输出所有可能的方案。
每个方案是7x5的矩阵。只有1和0组成。
矩阵中的列表示星期几,从星期一开始。
矩阵的行分别表示A,B,C,D,E的作息时间表。
多个矩阵间用空行分隔开。
例如,如下的矩阵就是一个合格的解。请编程输出所有解。
0110111
1101110
0110111
1101110
1110110
public class RiCheng
{
static int N = 0;
public static void show(int[][] a)
{
for(int i=0; i<5; i++)
{
for(int j=0; j<7; j++)
{
System.out.print(a[i][j]);
}
System.out.println();
}
}
public static void ping_jia(int[][] a)
{
// 连续工作不多于3天
for(int i=0; i<5; i++)
{
int sum = 0;
for(int j=0; j<14; j++)
{
if(a[i][j%7]==0)
sum = 0;
else
{
sum++;
if(sum>3) return;
}
}
}
// 至少有3天所有人都上班
int k = 0;
for(int i=0; i<7; i++)
{
if(a[0][i]==0 || a[1][i]==0 || a[2][i]==0 || a[3][i]==0 || a[4][i]==0) k++;
}
if(k==7||k==6||k==5) return;
// A B C D 中保证有2人上班
for(int i=0; i<7; i++)
{
int m = 0;
if(a[0][i]==1) m++;
if(a[1][i]==1) m++;
if(a[2][i]==1) m++;
if(a[3][i]==1) m++;
if(m<2) return;
}
// B D E 周日必须休息
if(a[1][6]==1 || a[3][6]==1 || a[4][6]==1) return;
// A E 周三必须上班
if(a[0][2]==0 || a[4][2]==0) return;
// A 与 C 一周中必须至少有4天能见面
k=0;
for(int i=0; i<7; i++)
{
if(a[0][i]==1 && a[2][i]==1) k++;
}
if(k<4) return;
N++;
show(a);
System.out.println("");
}
public static void f(int[][] a, int row)
{
if(row==5)
{
ping_jia(a);
return;
}
for(int i=0; i<7; i++)
{
for(int j=i+1; j<7; j++)
{
for(int k=0; k<7; k++) a[row][k] = 1;
a[row][i] = 0;
a[row][j] = 0;
f(a, row+1);
}
}
}
public static void main(String[] args)
{
int[][] a = {{1,1,1,1,1,0,0},{1,1,1,1,1,0,0},{1,1,1,1,1,0,0},
{1,1,1,1,1,0,0},{1,1,1,1,1,0,0},};
f(a,0);
System.out.println("N=" + N);
}
}更多推荐



所有评论(0)