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

 算法训练 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的情况开始,即线程数目大于2
		else 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=1
					for(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=w
				for(j=1;j<=i;j++)
				{
					for(k=0;k<a[j].val;k++)
						cout<<a[j].id<<" "<<a[j].id<<" ";
				}
				cout<<endl;
			}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值