meituan-005. 小美的区域会议
题目描述
小美是美团总部的高管,她想要召集一些美团的区域负责人来开会,已知美团的业务区域划分可以用一棵树来表示,树上有 n 个节点,每个节点分别代表美团的一个业务区域,每一个区域有一个负责人,这个负责人的级别为 A[i]
已知小美召集人员开会必须满足以下几个条件:
- 小美召集的负责人所在的区域必须构成一个非空的连通的图,即选取树上的一个连通子图。
- .这些负责人中,级别最高的和级别最低的相差不超过 k 。
请问小美有多少种召集负责人的方式,当且仅当选取的集合不同时我们就认为两种方式不同。由于方案数可能非常大,所以请对 10^9+7 取模。
输入:
- 输入第一行包含两个整数 n 和 k ,表示区域的数量,和不能超过的级别。
- 接下来有 n-1 行,每行有两个正整数 a 和 b ,表示 a 号区域和 b 号区域有一条边。
- 最后一行有 n 个整数,第 i 个整数表示 i 号区域负责人的级别。
输出:- 输出仅包含一个整数,表示可以选择的方案数对 10^9+7 取模之后的结果。
示例:
输入:
5 1
1 2
2 3
3 4
4 5
2 2 2 2 2
输出:15
解释:显然一个区域的方案有 {1},{2},{3},{4},{5},两个区域的方案有 4 个,三个区域的方案有 3 个,四个区域的方案有 2 个,五个区域的方案有 1 个,共 15 个。
解题思路
复盘
这题我的思路是以每个节点
i
i
i为根节点,dfs遍历,记录从根节点到
j
j
j的级别差记录到
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j].
之后遍历dp找小于
k
k
k的值.
后来发现我这只是从
i
i
i到
j
j
j的单条不重复路径, 对于重复路径的情况没考虑.
题解
参考题解枚举最小值+DFS, 以当前节点
i
i
i为权限最小的根节点建树, 记录可能的集合数
r
o
o
t
=
∏
(
n
o
d
e
i
+
1
)
root=\prod(node_i+1)
root=∏(nodei+1)即每个子节点可能的集合数
+
1
+1
+1后相乘,
+
1
+1
+1是因为可以选择不选择该子树.
注意, 当存在2个节点能连通且权限相等时, 这种情况下, 对这2个节点建树所组成的集合是完全相同的,所以需要去重.
import java.util.*;
public class Solution {
static Map<Integer,List<Integer>> map = new HashMap<>();
static int Mod = 1000000000+7;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int k = scan.nextInt();
for(int i=0;i<n-1;i++) {
int a = scan.nextInt();
int b = scan.nextInt();
List<Integer> l = map.getOrDefault(a, new LinkedList<>());
l.add(b);
map.put(a, l);
l = map.getOrDefault(b, new LinkedList<>());
l.add(a);
map.put(b, l);
}
int[] A = new int[n+1];
for(int i=0;i<n;i++) {
A[i+1] = scan.nextInt();
}
int ans=0;
//以1为最小权限建树,返回可能的种数
for(int i=1;i<=n;i++) {
boolean[] v = new boolean[n+1];
v[i]=true;
ans += dfs(i,A,i,k,v);
ans%=Mod;
}
System.out.println(ans);
}
private static int dfs(int node, int[] A,int i,int k,boolean[] v) {
long res=1;
List<Integer> l = map.getOrDefault(node, null);
if(l==null)
return (int) (res%Mod);
for(int addn:l) {
if(v[addn]) continue;
if(A[addn]<A[i]||A[addn]>A[i]+k)
continue;
if(A[addn]==A[i]&&addn<i)
continue;
v[addn]=true;
res*=(dfs(addn,A,i,k,v)+1);
v[addn]=false;
res%=Mod;
}
return (int) (res%Mod);
}
}
meituan-006. 小团的神秘暗号
题目描述
小团深谙保密工作的重要性,因此在某些明文的传输中会使用一种加密策略,小团如果需要传输一个字符串 S ,则他会为这个字符串添加一个头部字符串和一个尾部字符串。头部字符串满足至少包含一个 “MT” 子序列,且以 T 结尾。尾部字符串需要满足至少包含一个 “MT” 子序列,且以 M 开头。例如 AAAMT 和 MAAAT 都是一个合法的头部字符串,而 MTAAA 就不是合法的头部字符串。很显然这样的头尾字符串并不一定是唯一的,因此我们还有一个约束,就是 S 是满足头尾字符串合法的情况下的最长的字符串。
很显然这样的加密策略是支持解码的,给出一个加密后的字符串,请你找出中间被加密的字符串 S 。
输入:
- 输入第一行是一个正整数 n ,表示加密后的字符串总长度。
- 输入第二行是一个长度为 n 的仅由大写字母组成的字符串 T 。
输出:- 输出仅包含一个字符串 S 。
示例:
输入:
10
MMATSATMMT
输出:SATM
解题思路
复盘
简单模拟, 做得有点慢了.
题解
头尾遍历模拟即可
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
scan.nextLine();
String s = scan.nextLine();
boolean flag1=false,flag2=false;
int l=0,r=n-1;
while(l<r){
//头尾遍历
if(!flag1&&s.charAt(l)=='T') {
flag1 = check(s.substring(0, l+1));
}
if(!flag2&&s.charAt(r)=='M') {
flag2 = check(s.substring(r, n));
}
if(!flag1) {
l++;
}
if(!flag2) {
r--;
}
if(flag1&&flag2) {
System.out.println(s.substring(l+1,r));
return;
}
}
}
private static boolean check(String s) {
boolean flag=false;
boolean ans=false;
for(int i=0;i<s.length();i++) {
if(s.charAt(i)=='M')
flag=true;
if(flag&&s.charAt(i)=='T')
ans=true;
}
return ans;
}
}