A. 召唤神坤
先预处理出来每一个位置前后的最大值, 从前往后扫一下取max, 显然用 ( 前 m a x + 后 m a x ) a [ i ] \frac{(前max+后max)}{a[i]} a[i](前max+后max)对于当前位置来说其值是最大的.
import java.io.*;
public class A {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static StreamTokenizer stmInput = new StreamTokenizer(br);
static int N = 100010, INF = 0x3f3f3f3f;
static int a[] = new int[N], l[] = new int[N], r[] = new int[N];
static int n, max;
public static int readInt() throws IOException {
stmInput.nextToken();
return (int) stmInput.nval;
}
public static void solve() throws IOException{
n = readInt();
for(int i = 0; i < n; i++) a[i] = readInt();
max = -INF;
for(int i = 0; i < n; i++){
l[i] = max;
max = Math.max(max, a[i]);
}
max = -INF;
for(int i = n - 1; i >= 0; i--){
r[i] = max;
max = Math.max(max, a[i]);
}
int res = 0;
for(int i = 0; i < n; i++){
res = Math.max(res, (l[i] + r[i]) / a[i]);
}
pw.println(res);
}
public static void main(String[] args) throws IOException {
int T = 1;
while(T-- != 0){
solve();
}
pw.flush();
pw.close();
br.close();
}
}
B. 聪明的交换策略
从集合的角度考虑问题, 最终结果只有两种方案:
- 1111…000000…
- 0000…111111…
对于任意情况比如 00101001001, 对照上述两种情况进行交换, 比如移动1.
如果是第一种情况1111…000000…, 对于00101001001, 从前往后扫, 遇到的第 i d x idx idx个1就应该在第 i d x idx idx位上, 那么把它从第i位移动到第 i d x idx idx位所需要的最小操作次数就是 i − i d x i-idx i−idx, 累加即可.
第二种情况就是倒过来了.
import java.util.Scanner;
public class B {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String str = sc.next();
int idx = 0;
long ans1 = 0, ans2 = 0;
for(int i = 0; i < n; i++){
if (str.charAt(i) == '1'){
ans1 += i - idx;
idx++;
}
}
idx = n - 1;
for (int i = n - 1; i >= 0; i--){
if (str.charAt(i) == '1'){
ans2 += idx - i;
idx--;
}
}
System.out.println(Math.min(ans1, ans2));
sc.close();
}
}
这个题比较简单, 还有一类题型和这个长得比较像, 是给定三类值, 给定排好的序列, 问最少需要多少次交换, 感兴趣的同学可以看下, y总都有视频讲解:
整理书籍
交换座位
三值序列排序
C. 怪兽突击
如果要想打第i只怪兽, 那么得把它前i-1只怪兽都打过才行.
如果把前i只怪都打过, 很明显在前i只中选取 m i n ( a [ i ] + b [ i ] ) min(a[i]+b[i]) min(a[i]+b[i]), 剩下的次数都打这个怪.
由于前面的限制, 所以需要累加一下前面的sum, 同时每次取 m i n ( a [ i ] + b [ i ] ) min(a[i]+b[i]) min(a[i]+b[i])即可
import java.io.*;
public class C {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static StreamTokenizer stmInput = new StreamTokenizer(br);
static int N = 100010;
static int a[] = new int[N], b[] = new int[N];
static int n, k;
public static int readInt() throws IOException {
stmInput.nextToken();
return (int) stmInput.nval;
}
public static void solve() throws IOException{
n = readInt();
k = readInt();
for (int i = 1; i <= n; i++) a[i] = readInt();
for (int i = 1; i <= n; i++) b[i] = readInt();
long sum = 0, min = (long) 1e18, ans = (long) 1e18;
// 前i天的怪都打过一次后, 自然是在这前i天中找最小的(a[i]+b[i]), 用剩下的k-i次去打
for (int i = 1; i <= Math.min(n, k); i++){
sum += a[i];
min = Math.min(min, a[i] + b[i]);
ans = Math.min(ans, sum + (k - i) * min);
}
pw.println(ans);
}
public static void main(String[] args) throws IOException {
int T = 1;
while(T-- != 0){
solve();
}
pw.flush();
pw.close();
br.close();
}
}
D. 蓝桥快打
如果小蓝先手要赢, 那么他要使得 他攻击小桥的次数<=小桥攻击他的次数, 假设小蓝的攻击力为d, 那么:
小蓝攻击小桥并且小蓝获胜所需的最小次数: x = ⌈ 小桥的体力 B 小蓝的攻击力 D ⌉ x=\lceil \frac{小桥的体力B}{小蓝的攻击力D} \rceil x=⌈小蓝的攻击力D小桥的体力B⌉
小桥攻击小蓝并且小桥获胜所需的最小次数: y = ⌈ 小蓝的体力 A 小桥的攻击力 C ⌉ y=\lceil \frac{小蓝的体力A}{小桥的攻击力C} \rceil y=⌈小桥的攻击力C小蓝的体力A⌉
很明显, 只要找到最小的d, 使得 x < = y x<=y x<=y, 二分答案即可
import java.io.*;
public class D {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static StreamTokenizer stmInput = new StreamTokenizer(br);
static int n, m;
public static int readInt() throws IOException{
stmInput.nextToken();
return (int) stmInput.nval;
}
public static void solve() throws IOException {
double a = readInt(), b = readInt(), c = readInt();
int l = 1, r = (int) 1e9 + 10;
while (l < r){
int mid = l + r >> 1;
if (Math.ceil(b / mid) <= Math.ceil(a / c)) r = mid;
else l = mid + 1;
}
pw.println(r);
}
public static void main(String[] args) throws IOException {
int T = readInt();
while(T-- != 0){
solve();
}
pw.flush();
pw.close();
br.close();
}
}
关于二分, 可以看下我下面的总结, 感觉应该对大家有所帮助:
// 二分模板一共有两个,分别适用于不同情况。
// 算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。
// 模板1(check左边)(取右边界)
// 当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。
public static void f1(int l,int r){
while(l<r){
int mid = l + r + 1 >> 1;
if(check(mid)){
l = mid;
}else{
r = mid - 1;
}
}
return l;
}
// 对于模板1, 因为是check左边, 取右边界, 所以mid及其左边的所有值都满足条件.
// 一般这种是题目要求取"最大的最小值", "可能取小了"和"一定取大了"
// 1.如果check满足条件,l=mid, 说明"可能取小了";
// "可能" 是指(此时的mid是满足条件的, 有可能存在比它更大的数也满足要求), "取小了"是指(可能存在比此时mid更大的值也满足check要求, 这时还会继续二分往mid右边去查)
// 2反之, check不满足条件,r=mid-1, 说明"一定取大了"
// "一定"是指(此时的mid一定是不满足要求的), "取大了"是指(此时mid不满足要求, 要从mid-1往左去找)
// 模板2(check右边)(取左边界)
// 当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。
public static void f2(int l,int r){
while(l<r){
int mid = l + r >> 1;
if(check(mid)){
r = mid;
}else{
l = mid + 1;
}
}
return l;
}
// 同理, 一般这里要求取"最小的最大值", "可能取大了"和"一定取小了", 请自行分析
E. 奇怪的段
动态规划, 线性dp
f [ i ] [ j ] f[i][j] f[i][j]表示将前i个数划分成j段的所有方案的集合.
- 对于第i个元素, 它属于上一段. f [ i − 1 ] [ j ] + a [ i ] ∗ p [ j ] f[i-1][j]+a[i]*p[j] f[i−1][j]+a[i]∗p[j]
- 对于第i个元素, 从它开始新开一段. f [ i − 1 ] [ j − 1 ] + a [ i ] ∗ p [ j ] f[i-1][j-1]+a[i]*p[j] f[i−1][j−1]+a[i]∗p[j]
二类取max即可
由于有负数, 需要进行初始化f
根据定义初始化 f [ 0 ] [ 0 ] = 0 f[0][0]=0 f[0][0]=0
在做这个题之前, 最好是做过: 最大连续子序列
import java.io.*;
import java.util.*;
public class E {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static StreamTokenizer stmInput = new StreamTokenizer(br);
static int N = 100010, M = 210;
static int a[] = new int[N], p[] = new int[M];
static long f[][] = new long[N][M], INF = (long) 1e18;
static int n, m;
public static int readInt() throws IOException{
stmInput.nextToken();
return (int) stmInput.nval;
}
public static void solve() throws IOException {
n = readInt();
m = readInt();
for(int i = 1; i <= n; i++) a[i] = readInt();
for(int i = 1; i <= m; i++) p[i] = readInt();
for(int i = 0; i < N; i++) Arrays.fill(f[i], -INF);
f[0][0] = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++){
f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - 1]) + (long) a[i] * p[j];
}
}
pw.println(f[n][m]);
}
public static void main(String[] args) throws IOException {
int T = 1;
while(T-- != 0){
solve();
}
pw.flush();
pw.close();
br.close();
}
}
F. 小蓝的反击
分解质因数+双指针