蓝桥杯算法提高

蓝桥杯算法提高

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)。
输入格式
  一行,包含一个整数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 
#include 
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【输出格式】
输出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<


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]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


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

思路:

这题给的时间有五秒那么多。
本题给出的一个图满足要求“使得任何一个大城市都能从首都直接或者通过其他大城市间接到达,同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的”,这说明给出的道路是一棵树。

根据花费,可计算出,当路程总长度为s时,需要的路费为s * (s+1) / 2 + 10 * s。这是关于s的一个单调递增函数,因此,当s越大时,答案也越大。所以要求的实际上是树中的最长路径。
使用传统的路径算法可以得到部分分,比如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
c++版:
#include 
#include 
#include 
#include 

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=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 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 lst2 = new Vector();
		lst2.addAll(lst);
		List lst3 = new Vector();
		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 lst = new Vector();
		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);
	}
}










 

你可能感兴趣的:(蓝桥之战)