原题链接
题目描述
给出N
个数字,找出小于等于X
的数字之和。
public static void solve() throws IOException{
int n = readInt(), x = readInt();
long s = 0;
for (int i = 0; i < n; i++) {
int p = readInt();
if (p <= x) s += p;
}
printWriter.println(s);
}
原题链接
题目描述
有一张一年有N
个月的日历,每个月有 D i D_i Di 天,如果第i
个月的第j
天,两个数字的每一位都相同,那么当天与当月是一个重数字日期。如第1
个月的第1
天和第11
天,两天都属于重数字日期,问N
个月中有多少个重数字日期。思路:暴力枚举
- 暴力检查每个月的每一天是否为重数字日期。
public static void solve() throws IOException {
int n = readInt();
int[] days = new int[n];
for (int i = 0; i < n; i++) {
days[i] = readInt();
}
int sum = 0;
for (int i = 1; i <= n; i++) {// 枚举月
for (int j = 1; j <= days[i - 1]; j++) {// 枚举日
String s1 = String.valueOf(i), s2 = String.valueOf(j);
boolean f = true;
for (int a = 0; a < s1.length(); a++) {
for (int b = 0; b < s2.length(); b++) {
if (s1.charAt(a) != s2.charAt(b)) {
f = false;
break;
}
}
if (!f) break;
}
if (f) sum++;
}
}
printWriter.println(sum);
}
原题链接
题目描述
给定一个长度为N
的由小写字母组成的字符串,再给出Q
个查询,每次查询由两个整数 [ l , r ] [l, r] [l,r] 组成,对于每次询问,查询 S l , S l + 1 , . . . S r S_l,S_{l+1},...S_r Sl,Sl+1,...Sr 中总共有多少个连续的两个相同的小写字母。思路:动态规划
- dp数组的含义为以第 i i i 个字符结尾,从 1 ∼ i 1 \sim i 1∼i 中总共有多少个连续的两个相同的小写字母。
public static void solve() throws IOException {
int n = readInt(), q = readInt();
String s = (" " + readString());
int[] dp = new int[n + 10];
dp[1] = 0;
for (int i = 2; i <= n; i++) {
if (s.charAt(i) == s.charAt(i - 1)) {
dp[i] = dp[i - 1] + 1;
} else {
dp[i] = dp[i - 1];
}
}
while (q-- > 0) {
int l = readInt(), r = readInt();
printWriter.println(dp[r] - dp[l]);
}
}
原题链接
题目描述
给定一个由ABC
组成的字符串S
,只要S
中包含子串ABC
,就要进行以下的操作:从左边开始删除子串ABC
,删除后,剩下的子串进行拼接,重复执行以上的操作。输出最终的字符串。思路:栈模拟
- 从左到右,字符入栈时,先进行以下操作:判断栈顶字符是否为
C
,如果为C
,那么弹出栈顶的三个字符,判断三个字符是否为子串ABC
,如果是,那么继续执行上面的操作;如果不是,则将刚刚弹出的元素重新入栈,但要注意入栈顺序。
public static void solve() throws IOException {
String s = readString();
Deque<Character> deque = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
deque.push(s.charAt(i));
while (deque.size() >= 3 && deque.peek() == 'C') {
char ch1 = deque.pop(), ch2 = deque.pop(), ch3 = deque.pop();
if (ch1 != 'C' || ch2 != 'B' || ch3 != 'A') {
deque.push(ch3);
deque.push(ch2);
deque.push(ch1);
break;
}
}
}
StringBuilder sb = new StringBuilder();
while (deque.size() > 0) {
sb.append(deque.pop());
}
printWriter.println(sb.reverse().toString());
}
原题链接
题目描述
给定N
个顶点、M
条无向边和一个模数K
,每条边由一个三元组 ( u , v , w ) (u,v,w) (u,v,w) 组成,表示顶点u
和顶点v
的权重为w
,请根据这M
条边构成最小生成树T
,代价为各边权重之和,并对K
取模,请求出构成T
的最小成本。思路:并查集+dfs
- 该题无法通过最小生成树算法实现,因为每次累加权重和时,要对
K
进行取模,比如点 1 → 2 1 \to 2 1→2的权重为 6 6 6, 2 → 3 2 \to 3 2→3权重为 3 3 3, 2 → 4 2 \to 4 2→4权重为 4 4 4, 3 → 4 3 \to 4 3→4权重为 1 1 1,模数为 11 11 11, 如果使用最小生成树算法得最小代价为 10 10 10,实际结果为 0 0 0。- 由于边数较少,我们可以爆搜,从
M
条边中选出N-1
条边,再通过并查集判断N
个点是否处于同一联通块,形成生成树。枚举每一种方案,时间复杂度最坏是 O 28 7 O_{28}^{7} O287。
static int n, m;
static int[] p;
static long mod, res = Long.MAX_VALUE;
static List<Edge> edges;
static boolean[] st;
public static void solve() throws IOException {
n = readInt(); m = readInt();
mod = readLong();
edges = new ArrayList<>();
st = new boolean[m + 1];
for (int i = 0; i < m; i++) {
edges.add(new Edge(readInt(), readInt(), readLong()));
}
dfs(0, 0);
printWriter.println(res);
}
// cnt表示当前选择了多少条边,cur表示当前选中的最后一条边
public static void dfs(int cnt, int cur) {
if (cnt > n - 1) return;
// n个点,n-1条边
if (cnt == n - 1) {
p = new int[n + 1];
for (int i = 1; i <= n; i++) {
p[i] = i;
}
long curSum = 0;
for (int i = 0; i < m; i++) {
if (st[i]) {// 这条边被选中了
int u = edges.get(i).pe, v = edges.get(i).to;
long w = edges.get(i).w;
int pu = find(u), pv = find(v);
p[pu] = pv;// 合并
curSum = (curSum + w) % mod;
}
}
boolean ok = true;
int p1 = find(1);
// 判断 n个点是否在一个联通块
for (int i = 1; i <= n; i++) {
if (find(i) != p1) {
ok = false;
return;
}
}
if (ok) {
res = Math.min(res, curSum);
}
return;
}
for (int i = cur; i < m; i++) {
st[i] = true;
dfs(cnt + 1, i + 1);
st[i] = false;
}
}
public static int find (int x) {
if (x != p[x]) {
p[x] = find(p[x]);
}
return p[x];
}
原题链接
题目描述
给定一个整数N
和Q
个三元组 ( a , b , d ) (a,b,d) (a,b,d),并且初始化一个空集合S
,从头开始遍历Q
个三元组,如果存在一个整数序列 ( X 1 , X 2 , . . . X N ) (X_1,X_2,...X_N) (X1,X2,...XN),使得集合S
中的所有三元组都满足 X a i − X b i = d i X_{a_i} - X_{b_i} = d_i Xai−Xbi=di,对于所有的 i ∈ S i \in S i∈S,那么当前三元组会被添加至集合S
,按升序数组最终集合S
中所有三元组的编号。思路:带权并查集
- 遍历每一个三元组时,判断
a
和b
是否已经在同一联通块中,如果在,判断差值是否冲突;如果不在,那么一定可以构造出 X a − X b = d X_a - X_b = d Xa−Xb=d。
static int[] p;
static long[] val;
public static void solve() throws IOException{
int n = readInt(), q = readInt();
p = new int[n + 1];
val = new long[n + 1];
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 1; i <= q; i++) {
int a = readInt(), b = readInt(), d = readInt();
int pa = find(a), pb = find(b);
if (pa == pb) {
// 已经在一个联通块中,判断差值是否相等
if (val[a] - val[b] == d) {
printWriter.print(i + " ");
}
} else {
// 没在一个联通块中,那么当前三元组一定可以放入集合 S,并更新差值
p[pa] = pb;
val[pa] = d + val[b] - val[a];
printWriter.print(i + " ");
}
}
}
public static int find(int x) {
if (x != p[x]) {
int t = p[x];
p[x] = find(p[x]);
val[x] += val[t];
}
return p[x];
}