目录
一、算式900
讲解:
每天一题dfs题,这题和前面的每一天的第一题基本都差不多,这一次的数值从1-9变成了0-9,然后把原本的左右两边变成了左中右三块,并且第一块长度和第二块长度固定为4,第三块长度为2;
下面直接放代码,不会dfs的赶紧去背模板,然后刷刷这几天发过的题;
代码:
public class 全排列900 {
static int []arr={1,2,3,4,5,6,7,8,9,0};
static int count=0;
public static void main(String[] args) {
dfs(0);
}
public static void dfs(int step){
if (step==10) {
check();
return;
}
for (int i=step;i<arr.length;i++){
swap(i,step);
dfs(step+1);
swap(i,step);
}
}
public static void swap(int i,int j)
{
int t=arr[i];
arr[i]=arr[j];
arr[j]=t;
}
public static void check(){
if (arr[0]!=0&&arr[4]!=0&&arr[8]!=0) {//每个数的第一位不为0
int a = 0;
int b = 0;
int c = 0;
for (int i = 0; i < 4; i++) {//第一个数
a *= 10;
a += arr[i];
}
for (int i = 4; i < 8; i++) {//第二个数
b *= 10;
b += arr[i];
}
for (int i = 8; i < 10; i++) {//第三个数
c *= 10;
c += arr[i];
}
if ((a - b) * c == 900&&a!=5012) {//是否满足条件
System.out.println("("+a+"-"+b+")*"+c+"=900");
return;
}
}
}
}
二、谈判
讲解:
这题给定的条件很宽裕,数值也就1000,所以可以直接爆了;
但是爆也得选择正确的爆法,这题:每次选择两个,组成新的部落,那么,要消耗最少,每次只要选择最少的两个就好了
代码:
import java.util.Arrays;
import java.util.Scanner;
public class 谈判 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int []arr=new int[n];
for (int i=0;i<n;i++){
arr[i]=sc.nextInt();
}
Arrays.sort(arr);
long sum=0;
for (int i=0;i<n-1;i++){
sum+=arr[i]+arr[i+1];
arr[i+1]+=arr[i];//产生的新部落的人数
arr[i]=0;//旧部落人数归零
Arrays.sort(arr);//选完以后重新排序
}
System.out.println(sum);
}
}
三、幸运数
讲解:
这题和第二题一样,不过这里要注意一点:当你的某个值第一次判定就超过所剩余的数长度是,要结束判断;具体代码放在下面了
代码:
import java.util.Arrays;
import java.util.Scanner;
public class 幸运数 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
int []arr=new int[m+1];
int s=(m+1)/2;
for (int i=1;i<arr.length;i++){//省略第一次的把2的倍数去除
if (i%2==0)
arr[i]=m*10;
else
arr[i]=i;
}
Arrays.sort(arr);//按照剩余进行排序
int t;
for (int i=2;arr[i]<=s;i++){
t=arr[i];
for (int j=1;t*j<=s;j++){
arr[t*j]=m*10;//这个数实在对应的倍数下
}
s=s*(t-1)/t+1;//s去除了(1/当前序列)的值
Arrays.sort(arr);重新排序
}
int count=0;
for (int i=1;i<s*2;i++){//查找范围内的值
if (arr[i]>=m){
System.out.println(count);
return;
}
if (arr[i]>n){
count++;
}
}
System.out.println(count);
}
}
四、123
讲解:
这题其实就是前缀和+二分;当然,我这里是设置里两个前缀和,一个记录的是位置,一个记录的是值,如下:
1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5 6
那么arr[1]=1;arr[2]=3;arr[3]=6;arr[4]=10;arr[5]=15;arr[6]=21;
brr[1]=1;brr[2]=4;brr[3]=10;brr[4]=20;brr[5]=35;brr[6]=56;
然后用二分的方法分别找出两个端点所在的位置,判断两个端点在各自区间内可以得到的值,然后再加上两个端点中间的区间的值的总和即可;
具体讲解可能不太清楚,可以看看代码和代码注释
代码:
import java.util.Scanner;
public class 二分前缀123 {
static long []arr=new long[1500000];//存放位置最大值 (1+1500000)*1500000/2>1e12;
static long []brr=new long[1500000];//存放前n组总和
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();//组数
for (int i = 1; i < arr.length; i++) {//给定arr和brr数组内的值
arr[i] = arr[i - 1] + i;
brr[i] = brr[i - 1] + (long) (1 + i) *i/2;
}
long a, b, x, y;
long sum;
for (int i = 1; i <= t; i++) {
sum = 0;
a = sc.nextLong();//左端点
b = sc.nextLong();//右端点
x = ef(a);//a在第x行中
y = ef(b);//b在第y行中
if (x==y){//说明在同一个区间内
sum=(b-arr[Math.toIntExact(x - 1)]+1)*(b-arr[Math.toIntExact(x - 1)])/2;//求出右端点前的值
sum-=(a-arr[Math.toIntExact(x-1)])*(a-arr[Math.toIntExact(x-1)]-1)/2;//减去左端点前的值
}else {//说明不在同一个端点内
sum += ((brr[Math.toIntExact(x)] - brr[Math.toIntExact(x - 1)]) -
(a - arr[Math.toIntExact(x - 1)]) * (a - arr[Math.toIntExact(x - 1)] - 1) / 2);//计算左端点区间的值
sum += ((1 + b - arr[Math.toIntExact(y - 1)]) * (b - arr[Math.toIntExact(y - 1)]) / 2);//计算右端点区间的值
}
if (x+1<y)//说明两个端点中间有整区间
sum=sum+(brr[Math.toIntExact(y - 1)]-brr[Math.toIntExact(x)]);//加上两端点直接的所有区间值
System.out.println(sum);//输出
}
}
public static long ef(long a){
int min=1;
int max=1500000;
int mid;
while (min<max){
mid=(min+max)/2;
if (arr[mid]>=a&&arr[mid-1]<a){
return mid;//说明这个数在第mid行
}else if (arr[mid]>a){//说明这个值比它大
max=mid-1;//max变小
}else if (arr[mid]<a){//说明这个值比它小
min=mid+1;//min变大
}
}
return Math.min(min,max);//选择小值
}
}