一、
小易准备去魔法王国采购魔法神器,购买魔法神器需要使用魔法币
但是小易现在一枚魔法币都没有,但是小易有两台魔法机器可以通过投入x(x可以为0)个魔法币产生更多的魔法币。
魔法机器1:如果投入x个魔法币,魔法机器会将其变为2x+1个魔法币
魔法机器2:如果投入x个魔法币,魔法机器会将其变为2x+2个魔法币
小易采购魔法神器总共需要n个魔法币,所以小易只能通过两台魔法机器产生恰好n个魔法币
小易需要你帮他设计一个投入方案使他最后恰好拥有n个魔法币。
输入描述:
输入包括一行,包括一个正整数n(1 ≤ n ≤ 10^9),表示小易需要的魔法币数量。
输出描述:
输出一个字符串,每个字符表示该次小易选取投入的魔法机器。其中只包含字符’1’和’2’。
输入例子1:
10
输出例子1:
122
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//其实就是一个奇偶数问题
while(sc.hasNext()){
int n = sc.nextInt();
StringBuffer sb = new StringBuffer();
while(n != 0) {
if(n % 2 == 0) {
sb.append(2);
n -= 2;
n /= 2;
}else {
sb.append(1);
n -= 1;
n /= 2;
}
}
System.out.println(sb.toString().toString());
}
sc.close();
}
}
二、
魔法王国一共有n个城市,编号为0~n-1号,n个城市之间的道路连接起来恰好构成一棵树。
小易现在在0号城市,每次行动小易会从当前所在的城市走到与其相邻的一个城市,小易最多能行动L次。
如果小易到达过某个城市就视为小易游历过这个城市了,小易现在要制定好的旅游计划使他能游历最多的城市
请你帮他计算一下他最多能游历过多少个城市(注意0号城市已经游历了,游历过的城市不重复计算)。
输入描述:
输入包括两行,第一行包括两个正整数n(2 ≤ n ≤ 50)和L(1 ≤ L ≤ 100),表示城市个数和小易能行动的次数。
第二行包括n-1个整数parent[i](0 ≤ parent[i] ≤ i), 对于每个合法的i(0 ≤ i ≤ n - 2),在(i+1)号城市和parent[i]间有一条道路连接。
输出描述:
输出一个整数,表示小易最多能游历的城市数量。
输入例子1:
5 2
0 1 2 3
输出例子1:
3
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
/* 小易起点在0号城市,则小易可以到达所有a[i]为0的i+1号城市
* 到了i+1好城市,再找是否有a[j]为i + 1的j+1号城市
* 初始时已经能游历1座城市,最多能游历L+1座城市;否则只能游历最长连通的路径的城市
*/
//实际就是求树的最大深度的问题
while(sc.hasNext()){
int n = sc.nextInt();//城市的个数
int L = sc.nextInt();//行动次数
int[] parent = new int[n];//表示i+1和a[i]之间有道路
int[] dep = new int[n];//利用索引嵌套方式求最大深度
int max = 0;
for(int i = 1; i < n; i++) {//因为是i+1,所以从1开始
parent[i] = sc.nextInt();
dep[i] = dep[parent[i]] + 1;
max = Math.max(max, dep[i]);
}
System.out.println(process(max, L, n));
}
sc.close();
}
private static int process(int max, int L, int n) {
int res = 0;
if(max >= L) {//如果树高比L大,那么每一步都可以游历一座城市
res = L + 1;
}else {
//如果树高比L小,那么就要考虑折返的问题
res = max + 1;
res += (L - max) / 2;//多出的部分都要往返
}
res = Math.min(res, n);//重复游历无效
return res;
}
}
三、
小易正在玩一款新出的射击游戏,这个射击游戏在一个二维平面进行
小易在坐标原点(0,0),平面上有n只怪物,每个怪物有所在的坐标(x[i], y[i])。
小易进行一次射击会把x轴和y轴上(包含坐标原点)的怪物一次性消灭。
小易是这个游戏的VIP玩家,他拥有两项特权操作:
1、让平面内的所有怪物同时向任意同一方向移动任意同一距离
2、让平面内的所有怪物同时对于小易(0,0)旋转任意同一角度
小易要进行一次射击。小易在进行射击前,可以使用这两项特权操作任意次。
小易想知道在他射击的时候最多可以同时消灭多少只怪物,请你帮帮小易。
输入描述:
输入包括三行。
第一行中有一个正整数n(1 ≤ n ≤ 50),表示平面内的怪物数量。
第二行包括n个整数x[i](-1,000,000 ≤ x[i] ≤ 1,000,000),表示每只怪物所在坐标的横坐标,以空格分割。
第二行包括n个整数y[i](-1,000,000 ≤ y[i] ≤ 1,000,000),表示每只怪物所在坐标的纵坐标,以空格分割。
输出描述:
输出一个整数表示小易最多能消灭多少只怪物。
输入例子1:
5
0 -1 1 1 -1
0 -1 -1 1 1
输出例子1:
5
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
/* 只有50个点,在个数上不用太在意复杂度
* 任意垂直的两条直线可以经过旋转和平移变为y轴
* 遍历所有垂直的线,求落在这两条线上的点的个数
* 找出点最多的两条线
*/
while(sc.hasNext()){
int n = sc.nextInt();
int[] x = new int[n];
int[] y = new int[n];
for(int i = 0; i < n; i++) {
x[i] = sc.nextInt();
}
for(int i = 0; i < n; i++) {
y[i] = sc.nextInt();
}
int res = 0;
//先两个点确定一条线
for(int i = 0; i < n; i++) {
for(int j = i + 1; j < n; j++) {
//再找到其他任意点,作为垂直直线上的一点
for(int k = 0; k < n; k++) {
if(k != i && k != j) {
int cnt = 3;
for(int h = 0; h < n; h++) {
if(h != i && h != j && h != k) {
//h落在ij上,ih与ij斜率相同
if((x[j] - x[i]) * (y[h] - y[i]) == (y[j] - y[i]) *(x[h] - x[i])) {
cnt++;
}
//h落在另一条垂直线上,斜率为ij斜率的负数
else if((x[j] - x[i]) * (x[h] - x[k]) == -(y[h] - y[k]) * (y[j] - y[i])){
cnt++;
}
}
}
res = Math.max(res, cnt);
}
}
}
}
if(n < 4) {//如果只有三个点,那么必定垂直或在同一线上
System.out.println(n);
}else {
System.out.println(res);
}
}
sc.close();
}
}
四、
为了得到一个数的”相反数”,我们将这个数的数字顺序颠倒,然后再加上原先的数得到”相反数”。
例如,为了得到1325的”相反数”,首先我们将该数的数字顺序颠倒,我们得到5231,之后再加上原先的数,我们得到5231+1325=6556.
如果颠倒之后的数字有前缀零,前缀零将会被忽略。例如n = 100, 颠倒之后是1.
输入描述:
输入包括一个整数n,(1 ≤ n ≤ 10^5)
输出描述:
输出一个整数,表示n的相反数
输入例子1:
1325
输出例子1:
6556
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//利用StringBuffer的reverse快速颠倒,不用管前缀0
//相加应考虑进位问题
while(sc.hasNext()){
int n = sc.nextInt();
String str = String.valueOf(n);
StringBuffer sb = new StringBuffer(str);
String rev = sb.reverse().toString();
int len = str.length();
StringBuffer res = new StringBuffer();
int carry = 0;
for(int i = len - 1; i >= 0; i--) {
int tmp = rev.charAt(i) + str.charAt(i) - 2 * '0' + carry;
carry = 0;
if(tmp >= 10) {
tmp -= 10;
carry = 1;
}
while(tmp >= 10) {
tmp -= 10;
carry++;
}
res.append(tmp);
}
if(carry > 0) {
res.append(carry);
}
System.out.println(res.reverse().toString());
}
sc.close();
}
}
五、
小易有一个长度为N的正整数数列A = {A[1], A[2], A[3]…, A[N]}。
牛博士给小易出了一个难题:
对数列A进行重新排列,使数列A满足所有的A[i] * A[i + 1](1 ≤ i ≤ N - 1)都是4的倍数。
小易现在需要判断一个数列是否可以重排之后满足牛博士的要求。
输入描述:
输入的第一行为数列的个数t(1 ≤ t ≤ 10),
接下来每两行描述一个数列A,第一行为数列长度n(1 ≤ n ≤ 10^5)
第二行为n个正整数A[i](1 ≤ A[i] ≤ 10^9)
输出描述:
对于每个数列输出一行表示是否可以满足牛博士要求,如果可以输出Yes,否则输出No。
输入例子1:
2
3
1 10 100
4
1 2 3 4
输出例子1:
Yes
No
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
/* 其实还是奇偶问题
* 偶数一定是二的倍数,偶数×偶数一定是四的倍数
* 奇数只有×四才有可能是四的倍数
* 接下来就是怎么排列的问题
* n个奇数至少需要n-1个4的倍数,且不能有其余非4的倍数的偶数
* 如 1 4 3 4 5 4 7,则
*/
while(sc.hasNext()){
int t = sc.nextInt();
for(int ppp = 0; ppp < t; ppp++) {
int n = sc.nextInt();
int[] a = new int[n];
int four = 0;//四的倍数的个数
int evens = 0;//非四的倍数的偶数
int odds = 0;//奇数
for(int i = 0; i < n; i++) {
a[i] = sc.nextInt();
if(a[i] % 2 == 1) {
odds++;
}else if(a[i] % 4 == 0) {
four++;
}else {
evens++;
}
}
if(four < odds - 1) {
System.out.println("No");
}else if(four == odds - 1) {
if(evens == 0) {
System.out.println("Yes");
}else {
System.out.println("No");
}
}else {
System.out.println("Yes");
}
}
}
sc.close();
}
}
六、
一个由小写字母组成的字符串可以看成一些同一字母的最大碎片组成的。
例如,”aaabbaaac”是由下面碎片组成的:’aaa’,’bb’,’c’。
牛牛现在给定一个字符串,请你帮助计算这个字符串的所有碎片的平均长度是多少。
输入描述:
输入包括一个字符串s,字符串s的长度length(1 ≤ length ≤ 50),s只含小写字母(‘a’-‘z’)
输出描述:
输出一个整数,表示所有碎片的平均长度,四舍五入保留两位小数。
如样例所示: s = “aaabbaaac”
所有碎片的平均长度 = (3 + 2 + 3 + 1) / 4 = 2.25
输入例子1:
aaabbaaac
输出例子1:
2.25
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//连续子串问题
while(sc.hasNext()){
String str = sc.next();
double cnt = 1;
int len = str.length();
for(int i = 0; i < len - 1; i++) {
if(str.charAt(i) != str.charAt(i + 1)) {
cnt++;
}
}
System.out.println(String.format("%.2f", len / cnt));
}
sc.close();
}
}
七、
一个合法的括号匹配序列被定义为:
1. 空串”“是合法的括号序列
2. 如果”X”和”Y”是合法的序列,那么”XY”也是一个合法的括号序列
3. 如果”X”是一个合法的序列,那么”(X)”也是一个合法的括号序列
4. 每个合法的括号序列都可以由上面的规则生成,例如”“, “()”, “()()()”, “(()())”, “(((()))”都是合法的。
从一个字符串S中移除零个或者多个字符得到的序列称为S的子序列。
例如”abcde”的子序列有”abe”,”“,”abcde”等。
定义LCS(S,T)为字符串S和字符串T最长公共子序列的长度,即一个最长的序列W既是S的子序列也是T的子序列的长度。
小易给出一个合法的括号匹配序列s,小易希望你能找出具有以下特征的括号序列t:
1、t跟s不同,但是长度相同
2、t也是一个合法的括号匹配序列
3、LCS(s, t)是满足上述两个条件的t中最大的
因为这样的t可能存在多个,小易需要你计算出满足条件的t有多少个。
如样例所示: s = “(())()”,跟字符串s长度相同的合法括号匹配序列有:
“()(())”, “((()))”, “()()()”, “(()())”,其中LCS( “(())()”, “()(())” )为4,其他三个都为5,所以输出3.
输入描述:
输入包括字符串s(4 ≤ |s| ≤ 50,|s|表示字符串长度),保证s是一个合法的括号匹配序列。
输出描述:
输出一个正整数,满足条件的t的个数。
输入例子1:
(())()
输出例子1:
3
public class Main {
private static int res = 0;
private static int max = 0;
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//括号超时,毕竟递归里面再套了一个n^2复杂度的
while(sc.hasNext()){
String str = sc.next();
int len = str.length();
int cnt = 0;
for(int i = 0; i < len; i++) {
//统计括号对数
if(str.charAt(i) == '(') {
cnt++;
}
}
// //全排列
// StringBuffer sb = new StringBuffer();
// permutation(sb, str, cnt);
// System.out.println(res);
Set<String> set = new HashSet<>();
process(str, set);
System.out.println(set.size() - 1);//去掉自己
}
sc.close();
}
//因为最大子序列长度必定是原串长度-1,只需将任意括号更改位置再判断是否合法即可
private static void process(String str, Set<String>set) {
int len = str.length();
for(int i = 0; i < len; i++) {
StringBuffer sb = new StringBuffer(str);
sb.deleteCharAt(i);
char tmp = str.charAt(i);
for(int j = 0; j < len; j++) {
sb.insert(j, tmp);
if(isLegal(sb)) {
set.add(sb.toString());
}
sb.deleteCharAt(j);
}
}
}
//括号全排列
private static void permutation(StringBuffer sb, String str, int n) {
if(sb.length() == 2 * n) {
if(isLegal(sb) && ! sb.toString().equals(str)) {
//和原串逐个匹配
int cnt = length(str, sb);
if(cnt > max) {
res = 1;
max = cnt;
}else if(cnt == max) {
res++;
}
}
return;
}
//增加后回溯
sb.append('(');
permutation(sb, str, n);
sb.deleteCharAt(sb.length() - 1);
sb.append(')');
permutation(sb, str, n);
sb.deleteCharAt(sb.length() - 1);
}
//用于判断公共子序列长度
private static int length(String str, StringBuffer sb) {
int n = str.length();
int[][] dp = new int[n + 1][n + 1];
//为字符串A的第一个字符到第 i 个字符串和字符串B的第一个字符串到第 j 个字符串的最长公共子序列
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++){
if(str.charAt(i - 1) == sb.charAt(j - 1)){
dp[i][j] = dp[i - 1][j - 1] + 1;
}else{
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[n][n];
}
//用于判断是否合法
private static boolean isLegal(StringBuffer sb) {
Stack<Character> stk = new Stack<>();
int len = sb.length();
for(int i = 0; i < len; i++) {
if(sb.charAt(i) == '(') {
stk.push(sb.charAt(i));
}
if(sb.charAt(i) == ')') {
if(!stk.isEmpty() && stk.peek() == '(') {
stk.pop();
}else {
return false;
}
}
}
return stk.isEmpty();
}
}
八、
小Q和牛博士合唱一首歌曲,这首歌曲由n个音调组成,每个音调由一个正整数表示。
对于每个音调要么由小Q演唱要么由牛博士演唱,对于一系列音调演唱的难度等于所有相邻音调变化幅度之和
例如一个音调序列是8, 8, 13, 12, 那么它的难度等于|8 - 8| + |13 - 8| + |12 - 13| = 6(其中||表示绝对值)。
现在要对把这n个音调分配给小Q或牛博士,让他们演唱的难度之和最小,请你算算最小的难度和是多少。
如样例所示: 小Q选择演唱{5, 6}难度为1, 牛博士选择演唱{1, 2, 1}难度为2,难度之和为3,这一个是最小难度和的方案了。
输入描述:
输入包括两行,第一行一个正整数n(1 ≤ n ≤ 2000) 第二行n个整数v[i](1 ≤ v[i] ≤ 10^6), 表示每个音调。
输出描述:
输出一个整数,表示小Q和牛博士演唱最小的难度和是多少。
输入例子1:
23
24 13 2 4 54 23 12 53 12 23 42 13 53 12 24 12 11 24 42 52 12 32 42
输出例子1:
188
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//非连续相邻最小差
while(sc.hasNext()){
int n = sc.nextInt();
int[] a = new int[n];
for(int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
System.out.println(dynamic(a, n));
}
sc.close();
}
//用贪心算法只能局部最优,因此还是用动态规划
private static int greed(int[]a, int n) {
//第一个人固定持有第一个音
int last1 = 0;
int sum1 = 0;
//第二个人从第二个音开始试
int last2 = 0;
int sum2 = 0;
int res = 0xfffffff;
for(int i = 1; i < n; i++) {
last1 = a[0];
sum1 = 0;
last2 = a[i];
sum2 = 0;
for(int j = 1; j < n; j++) {
if(j == i) {
continue;
}
if(Math.abs(a[j] - last2) > Math.abs(a[j] - last1)) {
sum1 += Math.abs(a[j] - last1);
last1 = a[j];
}else {
sum2 += Math.abs(a[j] - last2);
last2 = a[j];
}
}
res = Math.min(res, sum1 + sum2);
}
return res;
}
private static int dynamic(int[]a, int n) {
// dp[i][j]为一个人最近唱第i个音,另一个人最近唱第j个音的最小难度
int[][] dp = new int[n + 1][n + 1];
for(int i = n - 1; i >= 0; i--) {
for(int j = n - 1; j >= 0; j--) {
int next = Math.max(i, j) + 1;//表示下一个音符
dp[i][j] = Math.min(dp[i][next]+(j==0?0:Math.abs(a[next-1]-a[j-1])),//给j唱
dp[next][j]+(i==0?0:Math.abs(a[next-1]-a[i-1])));//给i唱
}
}
return dp[0][0];
}
}