3.雀魂启动!
小包最近迷上了一款叫做雀魂的麻将游戏,但是这个游戏规则太复杂,小包玩了几个月了还是输多赢少。
于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且去除了一些特殊和牌方式(例如七对子等),具体的规则如下:
总共有36张牌,每张牌是1~9。每个数字4张牌。
你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌
14张牌中有2张相同数字的牌,称为雀头。
除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子的意思是递增的连续3个数字牌(例如234,567等),刻子的意思是相同数字的3个数字牌(例如111,777)
例如:
1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,7的4个刻子和9的雀头,可以和牌
1 1 1 1 2 2 3 3 5 6 7 7 8 9 用1做雀头,组123,123,567,789的四个顺子,可以和牌
1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法组成和牌的条件。
现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张牌中,再取一张牌,取到哪几种数字牌可以和牌。
输出描述:
输出同样是一行,包含1个或以上的数字。代表他再取到哪些牌可以和牌。若满足条件的有多种牌,请按从小到大的顺序输出。若没有满足条件的牌,请输出一个数字0
示例1
输入例子:
1 1 1 2 2 2 5 5 5 6 6 6 9
输出例子:
9
例子说明:
可以组成1,2,6,7的4个刻子和9的雀头
示例2
输入例子:
1 1 1 1 2 2 3 3 5 6 7 8 9
输出例子:
4 7
例子说明:
用1做雀头,组123,123,567或456,789的四个顺子
示例3
输入例子:
1 1 1 2 2 2 3 3 3 5 7 7 9
输出例子:
0
思路
遍历可加的九个数,然后进行递归判断。
ac代码
import java.util.*;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int[] card = new int[10];for (int i=0;i<13;i++)card[in.nextInt()]++;ArrayList<Integer> result = new ArrayList<>();for (int i=1;i<10;i++){if(card[i]>3)continue;card[i]++;if(findRight(card))result.add(i);card[i]--;}for (int x:result)System.out.print(x+" ");}public static boolean findRight(int[] card){boolean flog = false;for (int i=1;i<10;i++){// to delete queif (card[i]<2)continue;card[i]-=2;PriorityQueue<Integer> cards = new PriorityQueue<>();Add(cards,card);flog = proveRight(cards,card);card[i]+=2;if (flog)return true;}return false;}public static boolean proveRight(PriorityQueue<Integer> cards,int[] card){if (cards.isEmpty())return true;boolean flog = false;int now = cards.peek();if (card[now]>=3){for(int i=0;i<3;i++)cards.poll();card[now]-=3;flog = proveRight(cards,card);for(int i=0;i<3;i++)cards.add(now);card[now]+=3;}if (flog)return flog;if (now<=7&&card[now+1]!=0&&card[now+2]!=0){for(int i=0;i<3;i++){card[now+i]-=1;cards.remove(now+i);}flog = proveRight(cards,card);for(int i=0;i<3;i++){card[now+i]+=1;cards.add(now+i);}}return flog;}public static void Add(PriorityQueue<Integer> cards,int[] card){for (int i=1;i<10;i++){for (int x=1;x<=card[i];x++)cards.add(i);}}
}
4.特征提取
小明是一名算法工程师,同时也是一名铲屎官。某天,他突发奇想,想从猫咪的视频里挖掘一些猫咪的运动信息。为了提取运动信息,他需要从视频的每一帧提取“猫咪特征”。一个猫咪特征是一个两维的vector<x, y>。如果x_1=x_2 and y_1=y_2,那么这俩是同一个特征。
因此,如果喵咪特征连续一致,可以认为喵咪在运动。也就是说,如果特征<a, b>在持续帧里出现,那么它将构成特征运动。比如,特征<a, b>在第2/3/4/7/8帧出现,那么该特征将形成两个特征运动2-3-4 和7-8。
现在,给定每一帧的特征,特征的数量可能不一样。小明期望能找到最长的特征运动。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
第一行包含一个正整数N,代表测试用例的个数。
每个测试用例的第一行包含一个正整数M,代表视频的帧数。
接下来的M行,每行代表一帧。其中,第一个数字是该帧的特征个数,接下来的数字是在特征的取值;比如样例输入第三行里,2代表该帧有两个猫咪特征,<1,1>和<2,2>
所有用例的输入特征总数和<100000
N满足1≤N≤100000,M满足1≤M≤10000,一帧的特征个数满足 ≤ 10000。
特征取值均为非负整数。
输出描述:
对每一个测试用例,输出特征运动的长度作为一行
示例1
输入例子:
1
8
2 1 1 2 2
2 1 1 1 4
2 1 1 2 2
2 2 2 1 4
0
0
1 1 1
1 1 1
输出例子:
3
例子说明:
特征<1,1>在连续的帧中连续出现3次,相比其他特征连续出现的次数大,所以输出3.
思路
记录帧的所有值,并用一个map保存上一次的帧值,主要技巧在,用String保存两个long数据(其实根据题意无法判断long是否够存,所幸够,要不然在找值的时候需要再堆字符串进行处理)。
ac代码
import java.util.*;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int p = in.nextInt();while(p-->0){int n = in.nextInt();int max = Integer.MIN_VALUE;HashMap<String,Integer> before = new HashMap<>();HashMap<String,Integer> now = new HashMap<>();while(n-->0){int m = in.nextInt();for (int i=0;i<m;i++){long x = in.nextLong();long y = in.nextLong();String now_key = String.valueOf(x)+" "+String.valueOf(y);int now_value = before.getOrDefault(now_key,0)+1;max = Math.max(now_value,max);now.put(now_key,now_value);}before.clear();before.putAll(now);now.clear();}System.out.println(max);}}
}
5.毕业旅行问题(没做出来,思路贴了参考答案)
小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
城市个数n(1<n≤20,包括北京)
城市间的车票价钱 n行n列的矩阵 m[n][n]
输出描述:
最小车费花销 s
示例1
输入例子:
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出例子:
13
例子说明:
共 4 个城市,城市 1 和城市 1 的车费为0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,城市 1 和城市 4 之间的车费为 5,依次类推。假设任意两个城市之间均有单程票可购买,且票价在1000元以内,无需考虑极端情况。
思路
本来想的是进行回溯,后来计算复杂度是n的阶乘,肯定超时,但是没想到好的方法,找到了一个解析是用dp的这里贴一下解答。
思路就是用dp记录做每次加入单个城市路径的记录。
复杂度是O(2的n次方n的平方)
n最大20,10的六次方400 超时边界,基本不会超时。
ac代码
import java.util.*;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int n = in.nextInt();int[][] road = new int[n][n];for (int i=0;i<n;i++)for (int j=0;j<n;j++)road[i][j] = in.nextInt();// dp[i][j] ,replace the distance of i to(j) until 0;int[][] dp = new int[n][1<<(n-1)];for (int i=1;i<n;i++)dp[i][0] = road[i][0];for (int p=1;p<1<<(n-1);p++){for (int i=0;i<n;i++){dp[i][p] = Integer.MAX_VALUE>>1;if (is_p(i,p))continue;for (int now=1;now<n;now++){if (is_p(now,p)){int before = find(now,p);dp[i][p] = Math.min(dp[i][p],dp[now][before]+road[i][now]);}}}}System.out.println(dp[0][(1<<(n-1))-1]);}public static boolean is_p(int i,int p){ // 0 (为起始或者中间) 这件事一直都对return ((1<<(i-1))&p)!=0;}public static int find(int i,int p){
// return (p & (~(1 << (i - 1))));return p-(1<<(i-1));}}
7
思路:反向求解
如果是H(k+1) > E ,假设跳跃后为E_2,有 E_2 = E - (H(k+1) - E) = 2E - H(K+1);
如果是H(k+1) <= E,假设跳跃后为E_2,有 E_2 = E + (E - H(k+1)) = 2E - H(K+1);
无论哪种情况,最终都有 E_2 = 2E - H(K+1); -> E = (E_2 + H(k+1) + 1)/2 -> 加一是为了向上取整。
import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int n = in.nextInt();int[] nums = new int[n];for(int i=0;i<n;i++)nums[i] = in.nextInt();long result = 0;for(int i=n-1;i>=0;i--){result = (result+nums[i]+1)/2;}System.out.println(result);
}
}