蓝桥杯 MultiThreading(C++)

本文介绍了一种通过调度不同数量的加法循环来达到特定目标值W的算法。该算法涉及循环的顺序安排,并确保最终结果等于给定的目标值。文章详细解析了算法的工作原理、输入输出格式以及具体的实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

  • 问题描述

    现有如下一个算法:

    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

     

    来自 <http://lx.lanqiao.cn/problem.page?gpid=T347>

题目理解


  • 我们从输入的要求和解释可以看出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 执行。但是第一条语句和第二条语句之间是可以执行其他的过程N2N3,N3……..

代码思路

    • 不论在一个进程执行的 2条语句之间放多少过程语句,y 的值不会受到该过程中间那些其他的过程语句干扰,不明白的可以回顾题目中例子的执行步骤。
    • 期待值w1时:不论输入多少个元素,必须有一个元素是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过程消除其他过程
        • 第二种情况:求出sumw的差距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;

    }

结果截图

参考文献:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值