花已经枯了 浇再多的水也没用
C 直线
【题目描述】
【思路】
区域中的点两两组合,根据斜截式计算出每条直线的k和b。
难点在于对于 <k,b>怎么不重不漏地统计数量。
这里想到用 Map<Double, Set>,注意k、b可能为小数。相同的k映射到 同一个Set中,用Set判去重复的b。
package 第十二届;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* @author JohnnyLin
* @version Creation Time:2021年5月9日 上午8:52:55
* 类说明
*/
public class _C直线 {
// static int N = 2, M = 3;
static int N = 20, M = 21;
static Map<Double, Set<Double>> map = new HashMap<>();
//计算斜率
public static double getK(int x1, int y1, int x2, int y2) {
return (double) (y1 - y2) /(x1 - x2) ;
}
public static void main(String[] args) {
for(int i = 0; i < N; i ++) {
for(int j = 0; j < M; j ++) {
//(i, j) --> (x, y)
for(int x = 0; x < N; x ++) {
if( i == x) continue;
for(int y = 0; y < M; y ++) {
if( y == j) continue;
double k = getK(i, j, x, y);
double b = j - k * i;
if( !map.containsKey(k) ) {
System.out.println(k);
Set<Double> set = new HashSet<>();
set.add(b);
map.put(k, set);
}else {
Set<Double> set = map.get(k);
set.add(b);
}
}
}
}
}
int ans = 0;
for(Entry<Double, Set<Double>> entry: map.entrySet()) {
ans += entry.getValue().size();
}
//47753
System.out.println( ans + N + M);
}
}
package 第十二届;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
class Line{
double k, b;
public Line(double kk, double bb){
this.k = kk;
this.b = bb;
}
//重写equals方法: 比较k和b是否相等
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Line line = (Line) o;
return Double.compare(line.k, k) == 0 &&
Double.compare(line.b, b) == 0;
}
@Override
public int hashCode() {
//使用包装类
Double kk = this.k;
Double bb = this.b;
int result = 1;
result = 31 * result + (kk == null ? 0 : kk.hashCode());
result = 31 * result + (bb == null ? 0 : bb.hashCode());
return result;
}
}
public class _C直线2 {
static int N = 20, M = 21;
static Set<Line> set = new HashSet<>();
//计算斜率
public static double getK(int x1, int y1, int x2, int y2) {
return (double) (y1 - y2) /(x1 - x2) ;
}
public static void main(String[] args) {
for(int i = 0; i < N; i ++) {
for(int j = 0; j < M; j ++) {
//(i, j) --> (x, y)
for(int x = 0; x < N; x ++) {
if( i == x) continue;
for(int y = 0; y < M; y ++) {
if( y == j) continue;
double k = getK(i, j, x, y);
double b = j - k * i;
set.add(new Line(k, b) );
}
}
}
}
// 47753
System.out.println(set.size() + N + M);
}
}
看了一下其他大佬给的答案,我写的不对,据说是精度问题(四舍五入),所以结果多了。正确答案是:40257
正解
【思路】
需要通过Math.ads(k1-k2)>1e-8 || Math.ads(b1-b2)>1e-8 |来判断不同的直线。
排序
正确答案是:40257
package 第十二届.right;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
class Line implements Comparable<Line>{
double k, b;
public Line(double kk, double bb){
this.k = kk;
this.b = bb;
}
@Override
public int compareTo(Line o) {
if( Double.compare(this.k, o.k) == 0 ) return Double.compare(this.b, o.b);
return Double.compare(this.k, o.k);
}
}
public class _C直线3 {
static int N = 20, M = 21;
// static int N = 2, M = 3;
static Line[] q = new Line[1000000];
static double esp = 10E-8;
//计算斜率
public static double getK(int x1, int y1, int x2, int y2) {
return (double) (y1 - y2) /(x1 - x2) ;
}
public static void main(String[] args) {
int t = 0;
for(int i = 0; i < N; i ++) {
for(int j = 0; j < M; j ++) {
//(i, j) --> (x, y)
for(int x = 0; x < N; x ++) {
if( i == x) continue;
for(int y = 0; y < M; y ++) {
if( y == j) continue;
double k = getK(i, j, x, y);
double b = j - k * i;
q[t ++] = new Line(k, b);
}
}
}
}
//排序
Arrays.sort(q, 0, t);
int res = 1; //k :{1,1,2,2,3,3} {1,2,3}
for( int i = 1; i < t; i ++)
if( Math.abs(q[i].k - q[i - 1].k ) > esp || Math.abs(q[i].b - q[i - 1].b ) > esp)
res ++;
System.out.println(res + N + M);
}
}
D 货物摆放
【思路】
三个数a、b、c,乘积是n。n的约数中选三个,乘积是n的组合有多少。
当时写的时候觉得很难,觉得非dp写不出来。看了y总的解答后, 原来暴力也可以,是自己太菜了,多刷题、多刷题、多刷题。
重要事情讲三遍。
答案: 2430
package 第十二届;
import java.util.ArrayList;
import java.util.List;
/**
* @author JohnnyLin
* @version Creation Time:2021年5月9日 下午12:59:44
*/
public class _D货物摆放 {
static long N = 2021041820210418L;
static List<Long> f = new ArrayList<>();
public static void main(String[] args) {
/**
* N = 4时, i = 1 2
* N/i 4 2
*/
for(long i = 1; i * i <= N; i ++) {
if( N % i== 0) {
f.add(i);
//
if( N / i != i) f.add(N / i);
}
}
long ans = 0;
for(long a: f)
for(long b: f)
for(long c : f) {
if( a * b * c == N) ans++;
}
System.out.println(ans);
}
}
E路径
【思路】
spaf
package 第十二届;
import java.util.Arrays;
/**
* @author JohnnyLin
* @version Creation Time:2021年5月9日 下午1:43:36
*/
public class _E路径 {
static int N = 2200, M = N *50;//N: 点数 M:边数
static int h[] = new int [N];
static int e[] = new int [M];
static int w[] = new int [M];
static int ne[] = new int [M];
static int idx, n;
static int q[] = new int[N];
static int dist[] = new int[N];
static boolean st[] = new boolean[N];
static int INF = 0x3f3f3f3f;
public static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
public static void add(int a, int b, int c) {//添加一条边a -> b ,边的权重为c
e[idx] = b;
w[idx] = c;
ne[idx]= h[a];
h[a] = idx ++;
}
//求1号点到n号点的最短路径
public static void spfa() {
int hh = 0, tt = 0;
Arrays.fill(dist, INF);
dist[1] = 0;
q[tt ++] = 1;
st[1] = true;
while( hh != tt) {
int t = q[hh ++];
if( hh == N) hh = 0;
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if( dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
if (!st[j]) // 如果队列中已存在j,则不需要将j重复插入
{
q[tt ++ ] = j;
if (tt == N) tt = 0;
st[j] = true;
}
}
}
}
}
public static void main(String[] args) {
n = 2021;
Arrays.fill(h, - 1);
for(int i = 1; i <= n; i ++) {
for(int j = Math.max(1, i - 21); j <= Math.min(i + 21, n); j ++) {
//(i,j)节点
int d = gcd(i, j);
add(i, j, i * j / d);
}
}
spfa();
System.out.println(dist[n]);
}
}
时间显示
【题目描述】
【思路】
时间换算 注意 1s = 1000ms即可
import java.util.Scanner;
public class Main{
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
long time = reader.nextLong();
//1618708103123
//24*60 * 60s
// 1s = 1000ms
long fact = 3600 * 1000;
long f = 24 * fact; // 一天的毫秒数
long h = time % f / fact ;
// 1 min = 60 s
long m = time % f % fact/ 60000;
long s = time % f % fact % 60000 / 1000;
System.out.printf("%02d:%02d:%02d", h, m, s);
}
}
G 最少砝码
看了别人的题解,还是一脸懵逼
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
sc.close();
long w = 1L;
long i = 1L;
long s = 1L;
while (s < n) {
w *= 3;
s += w;
i++;
}
System.out.println(i);
}
}
H 杨辉三角
【思路】
找规律
组合数和杨辉三角关系:C(i,j)对应杨辉三角的第i行第j列(i从0开始, j从0开始)
写出杨辉三角:
发现左右对称( 因为 C(a, b) == C(a, a - b) ),左半边肯定是优先查找到目标数N的,所以只看左半边即可:
发现规律:
- 每一个斜行自右上到左下递增(即C(n,k), n逐行递增,k(斜行)不变,其结果C(n,k)递增)
- 同一横行中,从左到右递增,最右边最大且为组合数C(k, 2* k)
那么查找目标数N,即从最下边的斜线开始查找。第一个 找到的位置即为N第一次出现的位置。
根据组合公式: 32! / 16! /16! = 601 080 390
34!/17!/17!= 2 333 606 220 > 10e9,所以只需枚举前16个斜行,即k枚举到16即可。
因此可以直接从中间对称轴倒序二分查找:
二分的左右端点为:
左端点l:2*k
右端点r:max(n, l)
import java.util.Scanner;
public class Main {
static int n;
//计算组合数
public static long C(int a, int b){
long res = 1;
//C(a, b) = a! / (a - b)! / b! = a*(a - 1)*a(a - 2)……*a(a - b + 1) / b!
for(int i = a, j = 1; j <= b; j ++, i --){
res = res * i / j;
//该值已经大于n了 无意义且防止爆long
if( res > n) return res;
}
return res;
}
public static boolean check(int k){
//二分该斜行k
int l = 2* k, r = Math.max(n,l);
while( l < r){
int mid = l + r >> 1;
if( C(mid, k) >= n ) r = mid;
else l = mid + 1;
}
if( C(r, k) != n ) return false;
//C(r, k)对应编号 (r + 1) *r / 2 + k + 1
long ans = (long)(r + 1) *r /2 + k + 1;
System.out.println(ans);
return true;
}
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
n = reader.nextInt();
//从第16斜行开始枚举
for(int k = 16; ; k --){
if( check(k) ) break;
}
}
}