1 双核CPU
题目:一种双核CPU的两个核能够同时的处理任务,现在有n个已知数据量的任务需要交给CPU处理,假设已知CPU的每个核1秒可以处理1kb,每个核同时只能处理一项任务。n个任务可以按照任意顺序放入CPU进行处理,现在需要设计一个方案让CPU处理完这批任务所需的时间最少,求这个最小的时间。
链接:https://www.nowcoder.com/questionTerminal/9ba85699e2824bc29166c92561da77fa
来源:牛客网
输入描述:
输入包括两行:
第一行为整数n(1 ≤ n ≤ 50)
第二行为n个整数length[i](1024 ≤ length[i] ≤ 4194304),表示每个任务的长度为length[i]kb,每个数均为1024的倍数。
输出描述:
输出一个整数,表示最少需要处理的时间
示例:
输入
5
输出
9216
思路:这道题可以理解为一个01背包问题,双核CPU并行处理任务就是把所有任务分成两组,使时间最少就是让两组任务中花费时间较少的那组任务花费时间尽可能的接近串行总时间的一半,即求一个小于总时间一半的最大时间。使用动态规划求最优解,dp[i][j]表示前i个任务中总时间为j时可以进行任务的最大时间,dp[i][j] = max{dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]}。其中nums[i]表示第i个任务的时间;dp[i-1][j]表示前i-1个任务中总时间为j时进行任务的最大时间,即第i个任务不加入这一组;dp[i-1][j-nums[i]]表示前i-1个任务中总时间为j-nums[i]时进行任务的最大时间,避免加入nums[i]后超过总时间j。
代码:
public class Main {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int[] nums = new int[n];
int sum = 0;
for(int i=0;i<n;i++){
nums[i] = sc.nextInt()/1024;
sum += nums[i];
}
int[] dp = new int[sum/2+1];
for(int i=0;i<n;i++){
for(int j=sum/2;j>=nums[i];j--){
dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]);
}
}
System.out.println((sum-dp[sum/2]) * 1024);
}
}
}
2 魔力手环
题目:小易拥有一个拥有魔力的手环上面有n个数字(构成一个环),当这个魔力手环每次使用魔力的时候就会发生一种奇特的变化:每个数字会变成自己跟后面一个数字的和(最后一个数字的后面一个数字是第一个),一旦某个位置的数字大于等于100就马上对100取模(比如某个位置变为103,就会自动变为3).现在给出这个魔力手环的构成,请你计算出使用k次魔力之后魔力手环的状态。
链接:https://www.nowcoder.com/questionTerminal/79c639e02bc94e6b919e3372c8e1dc5e
来源:牛客网
输入描述:
输入数据包括两行:
第一行为两个整数n(2 ≤ n ≤ 50)和k(1 ≤ k ≤ 2000000000),以空格分隔
第二行为魔力手环初始的n个数,以空格分隔。范围都在0至99.
输出描述:
输出魔力手环使用k次之后的状态,以空格分隔,行末无空格。
示例:
输入
3 2
1 2 3
输出
8 9 7
思路:一开始暴力求解,结果就超时了,查了查发现可以用快速幂矩阵。快速幂的时间可以达到O(logn),比如
A156=A128∗A16∗A8∗A4
,156用二进制表示是10011100,和组成
A156
的乘数是对应的。回到这个题目本身,魔力手环每使用一次魔法每个数字会变成自己跟后面一个数字的和。
可以构造一个类似下面的矩阵A,每使用一次魔法就是用乘一次A,使用k次就是
Ak∗NT
(假设N为初始数组),再使用快速幂的方法求解
Ak
就好,大大减少了时间复杂度。
代码:
public class Main {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int k = sc.nextInt();
int nums[] = new int[n];
for(int i=0;i<n;i++){
nums[i] = sc.nextInt();
}
// while(k > 0){
// int f = nums[0];
// for(int i=0;i<n-1;i++){
// nums[i] = (nums[i] + nums[i+1])%100;
// }
// nums[n-1] = (nums[n-1] + f)%100;
// k--;
// }
//构造快速幂矩阵
int[][] r = new int[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if((i==j) || (j==i+1)){
r[i][j] = 1;
}else{
r[i][j] = 0;
}
}
}
r[n-1][0] = 1;
int[][] resMatrix = new int[n][n];
for(int i=0;i<n;i++){
resMatrix[i][i] = 1;
}
//快速幂
while(k != 0){
if((k & 1) == 1){
resMatrix = mulMatrix(resMatrix, r, n);
}
r = mulMatrix(r, r, n);
k = k >> 1;
}
//求结果
int[] res = new int[n];
for(int i=0;i<n;i++){
res[i] = 0;
for(int j=0;j<n;j++){
res[i] += resMatrix[i][j] * nums[j];
}
res[i] %= 100;
}
StringBuilder sb = new StringBuilder();
for(int i:res){
sb.append(i + " ");
}
System.out.println(sb.toString().trim());
}
}
private static int[][] mulMatrix(int[][] resMatrix, int[][] r, int n) {
int[][] res = new int[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
res[i][j] = 0;
for(int k=0;k<n;k++){
res[i][j] += resMatrix[i][k] * r[k][j];
}
res[i][j] %= 100;
}
}
return res;
}
}
3 堆砖块
题目:小易有n块砖块,每一块砖块有一个高度。小易希望利用这些砖块堆砌两座相同高度的塔。为了让问题简单,砖块堆砌就是简单的高度相加,某一块砖只能使用在一座塔中一次。小易现在让能够堆砌出来的两座塔的高度尽量高,小易能否完成呢。
链接:https://www.nowcoder.com/questionTerminal/040924ba0e64423b8a3fe2f75a56934a
来源:牛客网
输入描述:
输入包括两行:
第一行为整数n(1 ≤ n ≤ 50),即一共有n块砖块
第二行为n个整数,表示每一块砖块的高度height[i] (1 ≤ height[i] ≤ 500000)
输出描述:
如果小易能堆砌出两座高度相同的塔,输出最高能拼凑的高度,如果不能则输出-1.
保证答案不大于500000。
示例:
输入
3
2 3 5
输出
5
思路:本题同样使用动态规划。同样是将所有的砖块分为A、B两组,是两组的砖块数量相同且最高。构造dp[i][j]表示放入前i个砖块后,A和B的高度差为j时B的高度,j=B-A。那么dp[i][j] = max{dp[i-1][j], dp[i-1][j+h[i]], dp[i-1][j-h[i]]+h[i]},其中dp[i-1][j]表示第i个砖块既不放入A也不放入B;dp[i-1][j+h[i]]表示第i个砖块放入了A,这里需要解释一下,第i个砖块放入了A,B的高度是没变的,高度差减小了h[i],但是为了让他们的高度差仍然是j,此时B的高度是和放入第i-1块时高度差为j+h[j]时同高的;dp[i-1][j-h[i]]+h[i]表示第i个砖块放入了B,B的高度增加了h[i],高度差也增加了h[i],为了让他们的高度差仍然是j,所以用放入第i-1块时高度差为j-h[j]时B的高度加上h[i].
代码:
public class Main {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int[] h = new int[n+1];
int sum = 0;
for(int i=1;i<=n;i++){
int num = sc.nextInt();
h[i] = num;
sum += num;
}
int[][] dp = new int[2][sum*2+1];
Arrays.fill(dp[0], -1);
dp[0][sum] = 0;
int f = 1;
for(int i=1;i<=n;i++){
for(int j=0;j<=sum*2;j++){
dp[f][j] = dp[1-f][j];
if(j+h[i] <= sum*2 && dp[1-f][j+h[i]] != -1){
dp[f][j] = Math.max(dp[f][j], dp[1-f][j+h[i]]);
}
if(j-h[i] >= 0 && dp[1-f][j-h[i]] != -1){
dp[f][j] = Math.max(dp[f][j], dp[1-f][j-h[i]] + h[i]);
}
}
f = 1-f;
}
if(dp[1-f][sum] == 0){
System.out.println("-1");
}else{
System.out.println(dp[1-f][sum]);
}
}
}
}
4 分饼干
题目:易老师购买了一盒饼干,盒子中一共有k块饼干,但是数字k有些数位变得模糊了,看不清楚数字具体是多少了。易老师需要你帮忙把这k块饼干平分给n个小朋友,易老师保证这盒饼干能平分给n个小朋友。现在你需要计算出k有多少种可能的数值。
链接:https://www.nowcoder.com/questionTerminal/44d0ee89b51b4725b403d1df2381d2b2
来源:牛客网
输入描述:
输入包括两行:
第一行为盒子上的数值k,模糊的数位用X表示,长度小于18(可能有多个模糊的数位)
第二行为小朋友的人数n
输出描述:
输出k可能的数值种数,保证至少为1
示例:
输入
9999999999999X
3
输出
4
思路:这道题的目标是求k有多种可能的数值,可以使k除以n的余数为0。在除法的过程中,是从最高位开始除的,有余数的话会累加到下一位,那么我们可以将所有可能出现的余数存起来。构造一个二维数组dp[i][j]表示第i位(高位开始,从1记)余数为j的情况的数量,那么最后一位且除尽的可能数量就为dp[k.length][0]。
代码:
public class Main {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String str = sc.next();
char[] k = str.toCharArray();
int n = sc.nextInt();
long[][] dp = new long[k.length+1][n];
dp[0][0] = 1;
for(int i=1;i<=k.length;i++){
for(int j=0;j<n;j++){
for(int t=0;t<10;t++){
if(k[i-1] == 'X' || k[i-1] - '0' == t){
int m = (j * 10 + t) % n;
dp[i][m] += dp[i-1][j];
}
}
}
}
System.out.println(dp[k.length][0]);
}
}
}