蓝桥杯 算法训练 Multithreading (全面解释)

来源:互联网 发布:大数据时代 哪年来的 编辑:程序博客网 时间:2024/06/04 18:33
 算法训练 Multithreading  
时间限制:1.0s   内存限制:256.0MB
    
问题描述
  现有如下一个算法:
  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个操作循环分别[1]..[n].每个循环都有2个具体操作:1.y[i]=y; 2.y=y[i]+1;//可以发现当你第一次执行了某个[i]的第一步y[i]=y;的操作时,如果再执行[1]的第二步操作:y=y[i]+1,此时y的值只与y[i]有关//可试想如果在执行完第一步[i]操作后,中间有其他操作y的值肯定随着这些操作变化,但是最后当你再执行回[i]的第二步y=y[i]+1;操作时,y只进行了自增//由此我们可以知道,一次消去操作过后,肯定会对y++,而且用了一次[i]循环.//下面分了很多种情况,都有注释,最多我只用了2次消去操作。#include<iostream>#include<cstdlib>using namespace std;struct T{int val;int id;}a[101];int cmp(const T *a,const T *b){return (a->val - b->val)||(a->val == b->val);}int main(){int n,w,sum=0;cin>>n>>w;int i;for(i=1;i<=n;i++){cin>>a[i].val;a[i].id=i;}if(w<=0){cout<<"No"<<endl;return 0;}qsort((void*)(a+1),n,sizeof(T),(int (*)(const void *,const void *))cmp);//将乱序的操作变为有序的[1],[2]...[n]操作for(i=1;i<=n;i++){sum+=a[i].val;//利用sum,可以知道多余的操作 t=sum-w; 此时t为多余的循环,每一次循环都会对y++;if(sum>=w)break;}//此时[i]为最终需要运行的循环下标if(sum<w){cout<<"No"<<endl;return 0;}else if(n==1)//只有[1]线程的时候{if(a[1].val>w)//[1]的循环次数大于需求结果,肯定no,因为结果必然会大于w{cout<<"No"<<endl;return 0;}else//即a[1].val==w的时候,直接对[1]的所有循环都写出即可,那样结果就等于w了{int j;cout<<"Yes"<<endl;for(j=0;j<a[1].val;j++){cout<<a[1].id<<" "<<a[1].id<<" ";}return 0;}}//n>=2的情况开始,即线程数目大于2else if(w==1)//你需要求的结果是1的时候,那么i绝对是1,因为1000>=a[i].val>=1 啊{if(a[i].val==w)//当[1]线程的循环次数等于w时候,此时i==1是隐藏的{cout<<"Yes"<<endl;cout<<a[i].id<<" ";//y[1]=y=0,此时开始用[i]线程的一次循环去消去,其他所有操作int j,k;for(j=i+1;j<=n;j++){for(k=0;k<a[j].val;k++){cout<<a[j].id<<" "<<a[j].id<<" ";}}cout<<a[i].id<<endl;//y=y[1]+1=0+1=1;}if(a[i].val>w)//如果[1]线程的循环次数都比结果大,那么肯定是No的,因为消去除了[1]线程的多余循环(y++){//再对[1]线程多余的循环消去,y再次自增,此时y=2>w。[一次消去操作(循环)会对y++].无法进行2次消去操作cout<<"No"<<endl;}}//n>=2 && w>=2的情况开始讨论else if(i==1)//如果[1]线程已经满足sum>=w ,这里的情况与上面的情况有所不同,上面是w==1,这里w==2可以进行2次消去.{int j,k;cout<<"Yes"<<endl;if(w-2==0)//特殊情况 w==2&&a[1].val==2 的情况 {cout<<a[1].id<<" ";for(j=1;j<a[2].val;j++){cout<<a[2].id<<" "<<a[2].id<<" ";}for(j=3;j<=n;j++)//消去[2]线程后面的所有线程{for(k=0;k<a[j].val;k++)cout<<a[j].id<<" "<<a[j].id<<" ";}cout<<a[1].id<<" ";//第一次消去操作结束 y++;此时y=w-1=1;cout<<a[2].id<<" ";//第二次消去操作开始 y2=y=1for(j=0;j<a[1].val-(w-1);j++)//消去剩余的[1]线程循环{cout<<a[1].id<<" "<<a[1].id<<" ";}cout<<a[2].id<<endl;return 0;}else//一般情况{for(j=0;j<w-2;j++)//用w-2次循环[1]线程,将y=w-2; 后面还有2步消去操作,一次消去操作y++;{cout<<a[1].id<<" "<<a[1].id<<" ";}cout<<a[2].id<<" ";//没什么意义,只是应付答案(这条操作我想不出为什么会这样的).cout<<a[1].id<<" ";//用1次循环[1]线程,消去[2]多余操作,和所有其他多余操作,只将[2]保留一次循环用作第二次消去!cout<<a[2].id<<" ";for(j=1;j<a[2].val-1;j++)//上面[2]线程已经循环1次,所以a[2].val-1,需要剩1次[2]线程循环,所以j=1;{cout<<a[2].id<<" "<<a[2].id<<" ";}for(j=3;j<=n;j++)//消去[2]线程后面的所有线程操作,此时只有1次[2]循环,还有a[1].val-(w-1)次[1]循环{for(k=0;k<a[j].val;k++)cout<<a[j].id<<" "<<a[j].id<<" ";}cout<<a[1].id<<" ";//第一次消去操作结束 y++;此时y=w-1;cout<<a[2].id<<" ";//第二次消去操作开始for(j=0;j<a[1].val-(w-1);j++)//消去还剩下的[1]操作{cout<<a[1].id<<" "<<a[1].id<<" ";}cout<<a[2].id<<endl;//第二次消去操作结束 y++;此时y=w;return 0;}//你可以发现是整体用[1]去将y=w-1(其中从y=w-2变为y=w-1的过程有消去操作),//最后用1次[2]循环消去[1]多余的循环,并且对y++ 得到结果w}else//处理[1]线程满足不了sum>=w的情况,此时需要一次消去操作即可,即消去[i]多余的循环和[i]线程后面的所有线程,然后把有用的线程{//然后把[1]~[i]有用的线程输出,注意a[i].val=a[i].val-t;此时[i]线程只剩下有用的循环.cout<<"Yes"<<endl;int j,k;int t=sum-w;//a[i].val多余的操作a[i].val=a[i].val-t;//减去多余操作//用一个[1]操作去完成消去所有多余操作cout<<a[1].id<<" ";for(j=0;j<t;j++)//消去[i]的多余操作{cout<<a[i].id<<" "<<a[i].id<<" ";}for(j=i+1;j<=n;j++)//消去后面的所有多余操作{for(k=0;k<a[j].val;k++)cout<<a[j].id<<" "<<a[j].id<<" ";}cout<<a[1].id<<" ";//此时[1]已经用了一次循环a[1].val--;//开始遍历有用的操作使得y=wfor(j=1;j<=i;j++){for(k=0;k<a[j].val;k++)cout<<a[j].id<<" "<<a[j].id<<" ";}cout<<endl;}return 0;}

原创粉丝点击