题目
问题描述
现有如下一个算法:
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
题目理解
我们从输入的要求和解释可以看出N是数组的具体个数,w是期待值,也就是算法中y的值,然后数组里每个元素的值就代表了算法的循环次数(如N[1]=2的具体运算步骤为:y[1]=y=0,y=y[1]+1=1;y[1]=y=1,y=y[1]+1=2,这样执行2次算法,每次都是执行2条语句),不过一个执行过程的语句顺序不能变,例如执行了N1的第一条语句 y[1]=y=0 后,下次执行N1必须是接着第二条语句 y=y[1]+1 执行。但是第一条语句和第二条语句之间是可以执行其他的过程N2,N3,N3……..
代码思路
- 不论在一个进程执行的 2条语句之间放多少过程语句,y 的值不会受到该过程中间那些其他的过程语句干扰,不明白的可以回顾题目中例子的执行步骤。
- 期待值w为1时:不论输入多少个元素,必须有一个元素是1,否则肯定是不满足w的(注意这一点,我一直忽略了这种情况导致只有95分)
- 当只有一个元素时,只有该元素等于w一种情况才能满足w
- 当有多个元素时,我们先对元素从小到大排序,然后将元素值相加(从第一个元素开始相加),最后对相加结果sum分情况:
- Sum<0:不可能满足w
- Sum=0:肯定能满足w,只要sum里的元素之外的元素放到第一个过程的第一条语句和第二条语句中
- Sum>0:肯定能满足w,有2中可能:第一个元素大于w,前几个元素的结果大于w。
- 第一种情况:由于从小到大排序,所以N2值>N1值。先用一个N2过程消除使得N1过程只剩一个,然后将N2过程执行到 y=w-1,最后用唯一的N1过程消除其他过程
- 第二种情况:求出sum和w的差距t(t=sum-w),先用一个N1过程消除 t 的sum中最后一个元素多出的过程和sum之后的元素过程,然后开始执行剩下的过程。
代码实现
#include<iostream>
#include<cstdlib>
using namespace std;
struct Num{
int pos;
int value;
};
//比较函数
int comp(const void* pa,const void* pb){
Num* a=(Num*)pa;
Num* b=(Num*)pb;
if(a->value!=b->value){
return a->value-b->value;
}else{
return a->pos-b->pos;
}
}
const int maxLen=101;
int main(){
//输入数组长度,期待值,数组的值
int len,w;
cin>>len>>w;
Num nums[maxLen];
for(int index=1;index<=len;index++){
cin>>nums[index].value;
nums[index].pos=index;
}
if(w==1){
int numIndex=1;
for(;numIndex<=len;numIndex++){
if(nums[numIndex].value==1){
cout<<"Yes"<<endl;
//用值为1的数组消除其他数组
cout<<nums[numIndex].pos<<' ';
for(int index=1;index<=len;index++){
if(index!=numIndex){
for(int j=0;j<nums[index].value;j++){
cout<<nums[index].pos<<' '<<nums[index].pos<<' ';
}
}
}
cout<<nums[numIndex].pos<<' ';
return 0;
}
}
cout<<"No"<<endl;
return 0;
}
//如果只输入一个数,只有该数等于期待值才能满足条件
if(len==1){
if(nums[1].value==w){
cout<<"Yes"<<endl;
for(int index=0;index<nums[1].value;index++){
cout<<nums[1].pos<<' '<<nums[1].pos<<' ';
}
}else{
cout<<"No"<<endl;
}
}else{
//从小到大排序
qsort((void*)(nums+1),len,sizeof(Num),(int(*)(const void*,const void*))comp);
//数组索引从1开始,计算数组的和
int sum=0;
int numIndex=1;
//如果总数大于等于期待值就跳出
for(;numIndex<=len;numIndex++){
sum+=nums[numIndex].value;
if(sum>=w){
break;
}
}
//如果总数小于期待值肯定不能的
if(sum<w){
cout<<"No"<<endl;
}else if(sum==w){ //如果前面数的和刚好等于期待值
cout<<"Yes"<<endl;
cout<<nums[1].pos<<' ';
for(int j=numIndex+1;j<=len;j++){
for(int k=0;k<nums[j].value;k++){
cout<<nums[j].pos<<' ' <<nums[j].pos<<' ';
}
}
cout<<nums[1].pos<<' ';
//因为执行过一次
nums[1].value--;
for(int j=1;j<=numIndex;j++){
for(int k=0;k<nums[j].value;k++){
cout<<nums[j].pos<<' '<<nums[j].pos<<' ';
}
}
}else{
//第一个数就大于w,或几个数加起来才大于w
cout<<"Yes"<<endl;
if(numIndex==1){
cout<<nums[2].pos<<' ';
//只保留第一数组的1个
for(int index=0;index<nums[1].value-1;index++){
cout<<nums[1].pos<<' '<<nums[1].pos<<' ';
}
//3以后的数字都消除
for(int index=3;index<=len;index++){
for(int j=0;j<nums[index].value;j++){
cout<<nums[index].pos<<' '<<nums[index].pos<<' ';
}
}
cout<<nums[2].pos<<' ';
//因为执行过一次
nums[2].value--;
//把2执行到 y=w-1
for(int index=0;index<w-2;index++){
cout<<nums[2].pos<<' '<<nums[2].pos<<' ';
nums[2].value--;
}
cout<<nums[1].pos<<' ';
for(int index=0;index<nums[2].value;index++){
cout<<nums[2].pos<<' '<<nums[2].pos<<' ';
}
cout<<nums[1].pos<<' ';
}else{
int t=sum-w;
cout<<nums[1].pos<<' ';
//消除当前数组多出的循环
for(int index=0;index<t;index++){
cout<<nums[numIndex].pos<<' '<<nums[numIndex].pos<<' ';
nums[numIndex].value--;
}
//消除当前数组之后的循环
for(int index=numIndex+1;index<=len;index++){
for(int j=0;j<nums[index].value;j++){
cout<<nums[index].pos<<' '<<nums[index].pos<<' ';
}
}
cout<<nums[1].pos<<' ';
//因为执行过一次
nums[1].value--;
//把之前的都输出
for(int index=0;index<=numIndex;index++){
for(int j=0;j<nums[index].value;j++){
cout<<nums[index].pos<<' '<<nums[index].pos<<' ';
}
}
}
}
}
return 0;
}
结果截图
参考文献:
算法训练 Multithreading实现-知乎:https://www.zhihu.com/question/39646338