A.位数求和
解题思路
这道题没有什么算法可言,因为的范围很小,所以可以直接打暴力,唯一的优化就是不需要从1到指定位数的数字,而只需要从比这个数字的位数小一位的数字开始。
参考代码
import java.util.*;
public class Solution {
/**
* 返回这样的数之和
* @param n int整型 数的长度
* @param m int整型 各个为之和
* @return long长整型
*/
public long sum (int n, int m) {
// write code here
int mul = 1;
for(int i=1; i<=n; i++) mul *= 10;
long res = 0;
for(int i=mul/10; i<=mul; i++) {
int tmp = i;
int sum = 0;
while(tmp != 0) {
sum += tmp % 10;
tmp /= 10;
}
if(sum == m) res += i;
}
return res;
}
}A题扩展: 当题目中最大为100,这道题该如何解决呢?
解题思路
有了状态转移方程之后,我们还要考虑一个问题就是求出来的结果中是含有前导0的,可能有1个,2个或者n个,而这些数字的和正是f[n-1][0-9][m]表示n-1位,且第n-1位为0-9,和为m的所有数字的和。
参考代码
import java.util.*;
public class Solution {
/**
* 返回这样的数之和
* @param n int整型 数的长度
* @param m int整型 各个为之和
* @return long长整型
*/
int N = 110, M = 1010;
long[][][] f = new long[N][10][N]; // f[i, j, k]表示i位数字,最高位为j,前i位数字的和为k的所有数字的和
long[][][] cnt = new long[N][10][N]; // cnt[i, j, k]表示i位数字,最高位为j,前i位数字的和为k的数字的个数
public long sum (int n, int m) {
// write code here
for(int i=0; i<=9; i++){
f[1][i][i] = i;
cnt[1][i][i] = 1;
}
for(int i=2; i<=n; i++){
for(int j=0; j<=9; j++){
for(int k=j; k<=9*i; k++){
// 进行状态转移
for(int u=0; u<=9; u++){
f[i][j][k] += f[i-1][u][k-j] * 10 + cnt[i-1][u][k-j] * j;
cnt[i][j][k] += cnt[i-1][u][k-j];
}
}
}
}
long res = 0;
for(int i=0; i<=9; i++) {
res += f[n][i][m] - f[n-1][i][m];
}
return res;
}
}还有另一种方式实现不含前导0,就是我们可以发现,除了第一位不能填0之外,剩下的所有位置都可以填0,所以在枚举2位的情况时,要保证前一位的数字不为0就好了。
参考代码
import java.util.*;
public class Solution {
/**
* 返回这样的数之和
* @param n int整型 数的长度
* @param m int整型 各个为之和
* @return long长整型
*/
int N = 110, M = 1010;
long[][][] f = new long[N][10][N]; // f[i, j, k]表示i位数字,最高位为j,前i位数字的和为k的所有数字的和
long[][][] cnt = new long[N][10][N]; // cnt[i, j, k]表示i位数字,最高位为j,前i位数字的和为k的数字的个数
public long sum (int n, int m) {
// write code here
for(int i=0; i<=9; i++){
f[1][i][i] = i;
cnt[1][i][i] = 1;
}
for(int i=2; i<=n; i++){
for(int j=0; j<=9; j++){
for(int k=j; k<=9*i; k++){
// 进行状态转移
for(int u=(i == 2? 1 : 0); u<=9; u++){
f[i][j][k] += f[i-1][u][k-j] * 10 + cnt[i-1][u][k-j] * j;
cnt[i][j][k] += cnt[i-1][u][k-j];
}
}
}
}
long res = 0;
for(int i=0; i<=9; i++) res += f[n][i][m];
return res;
}
}B.不可思议
解题思路
这道题首先分析u[i]和v[i], 发现在构造的过程中,v[i]的值总是小于u[i]的,由于1号节点是根节点,所以最终的边一定是u[i] - > v[i]的,所以这里我们使用一个pre数组记录每一个点的前驱节点,然后对于每一次的询问,从当前节点出发,通过pre数组一直走到根节点,求出对应的结果即可
参考代码
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @param seed1 long长整型
* @param seed2 long长整型
* @param seed3 long长整型
* @param x int整型
* @return long长整型
*/
int MOD = 998244353, N = 100010, M = 2 * N;
int[] u = new int[N];
int[] v = new int[N];
int[] pre = new int[N]; // 存储每一个点的前驱节点
long ans(long x, long y){
long res = 0;
while(x != 1){
res += ((y + 2 * x) ^ (y + x));
x = pre[(int) x];
}
res += ((y + 2 * x) ^ (y + x));
return res;
}
public long work (int n, long seed1, long seed2, long seed3, int x) {
// write code here
long seed4 = 0;
for(int i=1; i<=n-1; i++){
seed4=(seed1+seed2)%MOD*seed3%MOD;
u[i]=i+1;
v[i]=(int)((seed4%i)+1);
pre[u[i]] = v[i];
// pre[v[i]] = u[i];
seed3=seed2;
seed2=seed1;
seed1=seed4;
}
long lastans = 0;
long ret = 0;
long y = 0;
long z = 0;
for(int i=1; i<=n; i++){
z=ans(x,y);
ret=(ret+z)%998244353;
lastans=z;
x=(int) (((x+lastans)^ret)%n+1);
y=lastans;
}
return ret;
}
}C. 牛牛晾衣服
解题思路
这道题首先我们可以通过分析得到,它具有二段性,所谓的二段性就指的是,当确定了答案为某一个值之后,在答案左边的都是不满足的,而答案右边的都是可以满足的,很明显,这道题是有的,之后我们考虑check函数怎么写,对于每一次二分出的限定时间lim,首先在这lim的时间内,所有的衣服都会自然的风干lim的水量,但是其中肯定会有一些衣服晾不干,因此需要烘***来烘干,而由于使用烘***其实是浪费了一次自然烘干的机会,所以对于当前的这一分钟,使用烘***烘干的水量实际上是k-1, 遍历整个数组,求出所需时间,如果大于lim,则返回0,否则返回1
参考代码
import java.util.*;
public class Solution {
/**
* 计算最少要多少时间可以把所有的衣服全烘干
* @param n int整型 n件衣服
* @param a int整型一维数组 n件衣服所含水量数组
* @param k int整型 烘***1分钟可以烘干的水量
* @return int整型
*/
boolean check(int lim, int[] a, int n, int k) {
int sum = 0;
for(int i=0; i<n; i++) {
if(a[i] <= lim) continue;
sum += Math.ceil((double)(a[i] - lim) / (k - 1));
if(sum > lim) return false;
}
return true;
}
public int solve (int n, int[] a, int k) {
// write code here
int max = a[0];
for(int i=0; i<n; i++) max = Math.max(max, a[i]);
if(k == 1) return max;
int l = 0; int r = max;
while(l < r) {
int mid = l + r >> 1;
if(!check(mid, a, n, k)) l = mid + 1;
else r = mid;
}
return l;
}
}
京公网安备 11010502036488号