1. 小团的神秘暗号

一字符串为其加上一个头部和尾部进行加密。头部至少包含一个“MT”的子序列,且以T结尾。尾部至少包含一个“MT”的子序列,且以M开始。求得取出头部、尾部后的最大字符串。

1
2
3
4
5
输入:
10
MMATSATMMT
输出:
SATM

送分题,找出最靠前的头部和最靠后的尾部,剩下的就是最长的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = Integer.valueOf(scan.nextLine());
String str = scan.nextLine();

int start = 0, end = n-1;
boolean flag1 = false, flag2 = false;

for(int i = 0; i < n; i++){
if(str.charAt(i) == 'M'){
flag1 = true;
}
if(str.charAt(i) == 'T' && flag1){
start = i+1;
break;
}
}

for(int i = n-1; i >= 0; i--){
if(str.charAt(i) == 'T'){
flag2 = true;
}
if(str.charAt(i) == 'M' && flag2){
end = i-1;
break;
}
}


System.out.println(str.substring(start, end+1));

}

2. 小团的选调计划

有n个人编号1-n,有n个任务,编号1-n。每个人可以填写选择任务的志愿,编号小的优先被满足。求出每个人最终分配的任务。

1
2
3
4
5
6
7
8
9
输入:
5
1 5 3 4 2
2 3 5 4 1
5 4 1 2 3
1 2 5 4 3
1 4 5 2 3
输出:
1 2 5 4 3

送分题,记录任务的领取状况,按人员编号从小到大,依次按其志愿领取任务未领取的任务。‘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[][] nums = new int[n][n];
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
nums[i][j] = scan.nextInt();
}
}

boolean[] selected = new boolean[n+1];
int[] res = new int[n];

for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(!selected[nums[i][j]]){
selected[nums[i][j]] = true;
res[i] = nums[i][j];
break;
}
}
}

for(int m : res){
System.out.print(m+" ");
}

}

3. 小团无路可逃

有一颗树,小团位于y位置,小美位于x位置。小美要追打小团,每次小美和小团可以向相邻位置转移(小团可以不动)。问最多多久小美可以追上小团。

1
2
3
4
5
6
7
8
输入:
5 1 2
2 1
3 1
4 2
5 3
输出:
2

求出小美距所有节点的最短距离[dx1, dx2, …],小团距所欲节点的最短距离[dy1, dy2, …]。则问题的结果是满足dxi > dyi的最大dxi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt(), x = scan.nextInt(), y = scan.nextInt();
List<Integer>[] lists = new List[n+1];
for(int i = 0; i < n-1; i++){
int u = scan.nextInt(), v = scan.nextInt();
lists[u] = lists[u] == null ? new ArrayList<>() : lists[u];
lists[v] = lists[v] == null ? new ArrayList<>() : lists[v];
lists[u].add(v);
lists[v].add(u);
}

int[] dist1 = getDistance(x, n, lists);
int[] dist2 = getDistance(y, n, lists);

int res = 0;
for(int i = 1; i <= n; i++){
if(dist1[i] > dist2[i]){
res = Math.max(res, dist1[i]);
}
}

System.out.println(res);


}
// 迪杰斯特拉算法求单源最短路径(还可以通过BFS算法求)
private static int[] getDistance(int start, int n, List<Integer>[] lists) {

int[] dist = new int[n+1]; // 节点编号1-n
boolean[] flag = new boolean[n+1];
Arrays.fill(dist, -1); // -1 代表不可达

if(lists[start] == null){
return dist;
}
for(int next : lists[start]){
dist[next] = 1;
}
flag[start] = true;

for(int i = 2; i <= n; i++){
// 找出下一个最近距离节点
int min = 0; // 初始
for(int j = 1; j <= n; j++){
if(!flag[j] && dist[j] != -1){ // 节点j尚未求出最短距离,且节点j目前可达
min = min == 0 ? j : dist[min] > dist[j] ? j : min;
}
}

flag[min] = true; // min节点加入最终集和
for(int next : lists[min]){ // 通过min,更新未求出最短距离的节点
if(!flag[next]){
if(dist[next] == -1){
dist[next] = dist[min] + 1;
}
else{
dist[next] = Math.min(dist[next], dist[min] + 1);
}
}
}
}

return dist;

}

3. 小团的默契游戏

一个长为n的整数数组,最大值不超过m。给一个组合(l, r),使得按序取数组中满足a[i]<l或a[i]>r组成的数组是单调不下降的。其中1<=l<=r<=m。

1
2
3
4
5
输入:
5 5
4 1 4 1 2
输出:
10

最简单的方式是通过三层循环解决该问题,但仔细分析可以发现一下规律:

  • 若(l, r)满足,则(l, r+1)肯定满足
  • 若(l, r)满足,则(l+1, *)满足的话,只能是*>=r

通过这两点可以进行充分的剪枝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int m = scan.nextInt(), n = scan.nextInt();
int[] nums = new int[n];
for(int i = 0; i < n; i++){
nums[i] = scan.nextInt();
}

int ct = 0;
int l, r, i;
int star = 1;

// 虽然n的数量级时10^5, 但加入了两处剪枝后,可以大幅降低时间复杂度
for(l = 1; l <= m; l++){
for(r = star; r <= m; r++){ // l时的r必然是l-1时r的子集---第二处剪枝
int pre = 0;
for(i = 0; i < n; i++){
if(nums[i] < l || nums[i] > r){
if(nums[i] < pre){
break;
}
pre = nums[i];
}
}
if(i == n){ // 子序列是非降序排列---第一处剪枝-1
break;
}
}
if(r <= m){ // [r, m]必然满足---第一处剪枝-2
ct += m - r + 1;
}
star = r;
}

System.out.println(ct);
}