目录

1 问题描述

2 解决方案

 


1 问题描述

问题描述
  现有如下一个算法:
  repeat ni times
  yi := y
  y := yi+1
  end repeat
  令n[1]为你需要算加法的第一个数字,n[2]为第二个,...n[N]为第N个数字(N为需要算加法的数字个数),
  并令y初始值为0,先令i=1运行这个算法(如上所示,重复n[i]次),然后令i=2运行这个算法。。直到i=N。注意y值一直不要清零。最后y的值就是你需要的加法答案。
  你想知道,有没有某种运算顺序能使答案等于W。
  一个循环中的全部语句,是不能改变在总的语句排列中的相对顺序的。
  (这里的第i个循环是指这n[i]*2条语句。就是你把属于第i个循环的语句抽出来看,它们需要按照原顺序排列。在你没有运行完这个循环的最靠前一条未完成的 语句的时候,你是不能跳过它先去完成这个循环后面的语句的。你能做的仅是把若干个循环按照你所规定的顺序“归并”起来。)
  举个例子,n[1]= 2 ,n[2]=1, W=1.一种可行的运算顺序是“2 1 1 1 1 2”,数字为几表示运行第几个算法的下一条语句(你可以看到”1”出现了4次,是因为n[1]=2即循环两次,而每次循环里面有两条语句,所以2*2=4次)

  y值 y[1] 值 y[2] 值
执行0条语句过后 0 0 0
执行1条过后(y[2]=y) 0 0 0
执行2条过后(y[1]=y) 0 0 0
执行3条过后(y=y[1]+1) 1 0 0
执行4条过后(y[1]=y) 1 1 0
执行5条过后(y=y[1]+1) 2 1 0
执行6条过后(y=y[2]+1) 1 1 0
       

  可以看到,最后y值变成了1,也就完成了我们的任务。

输入格式
  第一行你会得到用空格分开的两个整数N(1<=N<=100)和W(-10^9<=W<=10^9),(N为需要算加法的数字个数,W是你希望算出的数)。
  第二行你会得到n个整数n[i] (1<=n[i]<=1000).
输出格式
  第一行您应该输出Yes(若能以某种顺序使得这个算法得出W的值) 或No。
  如果第一行是No,接下来就不用继续输出了。
  如果是Yes, 请在第2行输出2*sigma(n[i])个用空格隔开的数,表示任意一种满足条件的运算顺序。
样例输入
1 10
11
样例输出
No
样例输入
2 3
4 4
样例输出
Yes
1 1 2 1 2 2 2 2 2 1 2 1 1 1 1 2
样例输入
3 6
1 2 3
样例输出
Yes
1 1 2 2 2 2 3 3 3 3 3 3
数据规模和约定
  对于30%的数据,n<=4, n[i]的和小于10.
  对于100%的数据,n<=100 , -10^9<=W<=10^9, 1<=n[i]<=1000

2 解决方案

初步看到此题,题意没看明白,前后仔细看了三四遍才整明白此题题意,竟无语吟噎......

此题意思一个形象的理解:输入n个数:N[0]、N[1]、...、N[n-1],和一个对照结果数w。把其中每一个N[i]看成一个进程,而N[i]具体值看成是这个进程所包含的线程数。例如,N[2] = 6,进程2有12个线程,如果结果输出Yes,则必定要输出12个2。为什么是12个2呢?因为执行一次循环,里面包含2个语句,6*2 = 12,所以进程2包含12个线程,且这12个线程的中间可以执行其它进程(例如:N[3])的线程,但是这12个线程的执行先后顺序不能打乱。不管有多少个进程,最后一个线程一定是执行y = y[i] + 1;即本题要求我们输出当y == w时的所有线程的一种可能执行顺序。

由y[i] = y; y = y[i] + 1;..执行规律可知,n个进程执行返回的最大y值maxY = N[0] + N[2] + ... +N[n-1]。(PS:因此,执行一次完整的循环,即执行两个语句,y的值自增1,且y的值可以向下传递)

那么,到这里,请看一种特例:当N[1] = 1时,y[1] = y = 0; ...(PS:除了进程1之外的所有进程均执行完毕)...,y = y[1] + 1 = 1,从而得到最后y的结果为1。

此时,我们可以初步想到y的结果一定满足 1 <= y <=maxY。

由题意中给定的w值输入范围,可以轻松得到大部分输出No的情况。

分析到这里,相信大家已经对于此题有了初步编码框架:即对w值进行划定区间,然后判断并找到相应输出结果。

但是,本文也有一点遗憾,在蓝桥杯练习系统里最终的测评分为95分,其中的测试数据5运行错误,由于没有VIP特权,不能查看输入和输出测试数据,我自己也查看了代码逻辑好大一会也没有检查出具体逻辑错误>~<。(PS:在本文最后给出的参考资料链接中,那位网友的代最终码运行结果是100分,大家也不妨参考一下~)

下面具体代码中判断Yes的情况核心思想:

先执行count = (w-2)*2个线程,返回此时的进程数i,这时可以轻松得知temp[i] = w -2,再执行除去进程i的所有线程数目总和减去2的结果的连续的线程,再执行一次进程i中的线程,这时y = temp[i]+1=w-1,再执行一次按照前后顺序排列的线程倒数第2个线程,出现temp[N.length-1] = y =w-1,然后执行完进程i中剩下的线程,最后执行按照前后顺序排列的线程的最后一个线程,返回y =  temp[N.length-1]+1 =w。

如果没看懂上面所说思想,下面代码注释应该能够看明白。

具体代码如下:

package com.liuzhen.systemExe;

import java.util.Scanner;

public class Main{
//返回数组N中的最小元素数组下标
public int getMinI(int[] N) {
int min = 0;
for(int i = 0;i < N.length;i++) {
if(N[i] < N[min])
min = i;
}
return min;
}
//执行第i个进程中的一个线程,执行完后,总线程数减1
public void runThread1(int[] N, int i) {
System.out.print((i+1)+" ");
N[i]--;
}
//从第i个进程开始,到第n个进程结束,顺序执行其中的每一个线程
public void runThread2(int[] N, int i, int n) {
for( ;i <= n;i++) {
while(N[i] > 0) { //执行第i个进程中的每一个线程
System.out.print((i+1)+" ");
N[i]--;
}
}
} public void printResult(int[] N, int w) {
int maxY = 0; //计算N个进程,依次执行能够得到的最大y值
for(int i = 0;i < N.length;i++) {
maxY += N[i];
N[i] *= 2; //此处乘以2表示,第i个线程需要执行的语句个数(因为执行一次循环,有两条语句)
} //(1)当w <= 0时,输出No
if(w <= 0)
System.out.println("No");
//(2)当w == 1时,此时要看N[i]的最小值,才能决定输入Yes或者No
else if(w == 1) {
int min = getMinI(N);
if(N[min] == 2) { //由于前面N[i] *= 2,此处N[min] == 2表示执行一次循环需要执行两句
System.out.println("Yes");
runThread1(N,min); //执行一次进程min中的第一个线程,此时y[0] = 0
if(min == 0) //执行完成除了进程min之外的所有进程
runThread2(N, 1, N.length-1);
else {
runThread2(N, 0, min-1);
runThread2(N, min+1, N.length-1);
}
runThread1(N,min); //执行进程min中的第二个线程,此次y = y[0] + 1 = 1
} else {
System.out.println("No");
} }
//(3)当数组N长度为1时,w不等于N[0]执行完整循环次数,输出No
else if(N.length == 1 && w > 1 && w < maxY) {
System.out.println("No");
}
//(4)当数组N长度为1时,w等于N[0]执行完整循环次数,输出Yes
else if(N.length == 1 && w == maxY) {
System.out.println("Yes");
while(N[0] > 0)
runThread1(N, 0);
}
//(5)当数组N的长度不等1且1 < w <= maxY时,此时一定会输出Yes
else if(N.length != 1 && w > 1 && w <= maxY) {
System.out.println("Yes");
/*
* 下面代码注解中tempY[i]表示题目中第i个进程的yi
* y表示题目循环中的y
*/
int count = (w-2)*2;
if(count == 0) {
int tempI = 0;
for(int i = 0;i < N.length-1;i++) {
if(N[i] >= 4) { //表示N[i]至少能够进行完整的2次循环,共四个语句
tempI = i;
break;
}
}
runThread1(N, tempI); //执行1次,此时tempY[tempI] = 0
if(tempI == 0)
runThread2(N, 1, N.length-2);
else {
runThread2(N, 0, tempI-1);
runThread2(N, tempI+1, N.length-2);
}
while(N[N.length-1] > 2) {
runThread1(N, N.length-1);
}
runThread1(N, tempI); //此时,y = tempY[tempI] + 1 = 1
runThread1(N, N.length-1); //此时,tempY[N.length-1] = y = 1
while(N[tempI] > 0) { //执行完N[tempI]中剩余的线程
runThread1(N, tempI);
}
runThread1(N, N.length-1); //此时执行最后一个线程,y = tempY[N.length-1] + 1 = 2
} else {
int i = -1;
//从第0个进程开始,执行到第i个进程,当tempY[i] = w-3, y = w - 2时结束
while(count > 0) {
i++;
while(N[i] > 0) {
runThread1(N, i); //此处执行两次runThread1表示执行一次整个循环,y值便会自增1
runThread1(N, i);
count = count - 2;
if(count == 0)
break;
}
}
runThread1(N, i); //执行完此句,此时tempY[i] = w - 2
runThread2(N, i+1, N.length-2); //执行进程i+1到 N.length-2之间的所有线程
while(N[N.length-1] > 2) { //执行最后一个进程中的线程
runThread1(N, N.length-1);
}
runThread1(N, i); //执行完此句,此时y = tempY[i] + 1 = w - 1
runThread1(N, N.length-1); //执行完此句,此时tempY[N.length-1] = y = w - 1
while(N[i] > 0) { //执行完进程i中剩余的线程
runThread1(N, i);
}
runThread1(N, N.length-1); //这是最后一个线程,执行完后,y = tempY[N.length-1] + 1 = w
}
}
//(6)当w > maxY时,输出No
else if(w > maxY) {
System.out.println("No");
}
} public static void main(String[] args){
Main test = new Main();
Scanner in = new Scanner(System.in);
// System.out.println("请输入一个数字n和一个数字w:");
int n = in.nextInt();
int w = in.nextInt();
// System.out.println("请输入n个数字:");
int[] N = new int[n];
for(int i = 0;i < n;i++)
N[i] = in.nextInt();
test.printResult(N, w); }
}

运行结果:

请输入一个数字n和一个数字w:
2 2
请输入n个数字:
2 2
Yes
1 2 2 1 2 1 1 2 请输入一个数字n和一个数字w:
3 6
请输入n个数字:
1 2 3
Yes
1 1 2 2 2 2 3 3 3 3 3 3 3

参考资料:

1. 蓝桥杯练习系统Multithreading

算法笔记_052:蓝桥杯练习Multithreading(Java)的更多相关文章

  1. 算法笔记_083:蓝桥杯练习 合并石子(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数.求把所有石子 ...

  2. 算法笔记_107:蓝桥杯练习 算法提高 学霸的迷宫(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗.但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要 ...

  3. 算法笔记_096:蓝桥杯练习 算法提高 求最大值(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 给n个有序整数对ai bi,你需要选择一些整数对 使得所有你选定的数的ai+bi的和最大.并且要求你选定的数对的ai之和非负,bi之和非负 ...

  4. 算法笔记_091:蓝桥杯练习 递推求值(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 已知递推公式: F(n, 1)=F(n-1, 2) + 2F(n-3, 1) + 5, F(n, 2)=F(n-1, 1) + 3F(n- ...

  5. 算法笔记_056:蓝桥杯练习 未名湖边的烦恼(Java)

    目录 1 问题描述 2 解决方案 2.1 递归法 2.2 递推法   1 问题描述 问题描述 每年冬天,北大未名湖上都是滑冰的好地方.北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰 ...

  6. 算法笔记_055:蓝桥杯练习 Tricky and Clever Password (Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 在年轻的时候,我们故事中的英雄——国王 Copa——他的私人数据并不是完全安全地隐蔽.对他来说是,这不可接受的.因此,他发明了一种密码,好 ...

  7. 算法笔记_076:蓝桥杯练习 结点选择(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 有一棵 n 个节点的树,树上每个节点都有一个正整数权值.如果一个点被选择了,那么在树上和它相邻的点都不能被选择.求选出的点的权值和最大是多 ...

  8. 算法笔记_060:蓝桥杯练习 出现次数最多的整数(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 编写一个程序,读入一组整数,这组整数是按照从小到大的顺序排列的,它们的个数N也是由用户输入的,最多不会超过20.然后程序将对这个数组进行统 ...

  9. 算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是 ...

随机推荐

  1. HDU3487 Play With Chain [Splay]

    题目传送门 题目描述 Problem Description YaoYao is fond of playing his chains. He has a chain containing n dia ...

  2. JavaScript函数的防抖和节流

    防抖 触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间 思路: 每次触发事件时都取消之前的延时调用方法 function debounce(fn) { let tim ...

  3. 洛谷——P2035 iCow

    P2035 iCow 题目描述 被无止境的农活压榨得筋疲力尽后,Farmer John打算用他在MP3播放器市场新买的iCow来听些音乐,放松一下.FJ的iCow里存了N(1 <= N < ...

  4. NGUI 简单的背包系统

    1.首先在场景中创建格子,用来存放物体的 2.为每一个格子设置标签为Item,建议只做一个格子,然后创建预制体就可以了,然后为每一个格子附加Box Collider组件,要用于检测嘛, 3.接下来就是 ...

  5. nyoj 300 (矩阵快速幂)Kiki & Little Kiki 2

    描述 There are n lights in a circle numbered from 1 to n. The left of light 1 is light n, and the left ...

  6. [BZOJ3997][TJOI2015]组合数学(Dilworth定理+DP)

    题目名字是什么就不能往那方面想. 每个点拆成a[i][j]个,问题变为DAG最小路径覆盖,由Dilworth定理转成最长反链. 使用Dilworth定理的时候要注意那些点之间有边,这里任意一个点和其右 ...

  7. [USACO 2018 Open Gold] Tutorial

    Link: 传送门 A: 对于每一条分割线,设本不应在其左侧的个数为$x$ 重点要发现每次一来一回的操作恰好会将一对分别应在左/右侧的一个数从右/左移过去 这样就转直接用树状数组求出最大的$x$即可 ...

  8. Codeforces 610C:Harmony Analysis(构造)

    [题目链接] http://codeforces.com/problemset/problem/610/C [题目大意] 构造出2^n个由1和-1组成的串使得其两两点积为0 [题解] 我们可以构造这样 ...

  9. [JSOI2018]军训列队

    [JSOI2018]军训列队 题目大意: \(n(n\le5\times10^5)\)个学生排成一排,第\(i\)个学生的位置为\(a_i\).\(m(m\le5\times10^5)\)次命令,每次 ...

  10. for循环、for-in、forEach、for-of四大循环

    平时自己在写一些小栗子的时候,用到的基本上是for循环,因为在学专业课(C,C++,JAVA,...)的时候用的最多的就是for循环,不过for循环的效率也是比较高的. 但是for循环在写的时候,涉及 ...