魔法师与扑克牌游戏 C++队列题目

开幕雷击!奥里给!!哈哈,这是我搜到的一位大佬的程序,但不是最优,会超时。

#include
using namespace std;
queue<int> q;
int ans[100010];
int main()
{
    int m;
    scanf("%d",&m);
    for(int i=m;i>=2;i--)
    {
        q.push(i);
        for(int j=1;j<=i%q.size();j++)
        {
            q.push(q.front());
            q.pop();
        }
    }
    int len=0;
    while(q.empty()==0)
    {
        ans[++len]=q.front();
        q.pop();
    }
    printf("1 ");
    for(int i=len;i>=1;i--)
        printf("%d ",ans[i]);
    
    return 0;
}

来看一下题目描述:

题目描述
魔法师在玩一种扑克牌游戏,n张扑克分别记上1,2,…,n。他打开第一张是1,把它放在一边。然后把最上面的两张一张一张地依次移到最后,打开上面一张刚好是2,再放在一边;然后把上面的3张一张一张移到最后,打开上面一张刚好是3,再放到一边;……如此重复下去,直到打开最后一张是n,放在一边,这时他发现,放在一边的扑克刚好是1,2,…n这样排列的。

请编程输出这些扑克原来是怎么排列的。

输入
1行,一个正整数n。

输出
1行,n个正整数,表示这些扑克牌原来的排列顺序,每两个数之间有一个空格。

样例输入
5
样例输出
1 4 5 2 3
提示
输入样例2:

9

输出样例2:

1 8 6 2 9 4 5 3 7

数据范围:

对于70%的数据:n<=100。

对于100%的数据:n<=10000.

上面那位大佬的程序会超时,在n=10000的地方肯定会超的。
苦思冥想后,我想出了一个优化的办法。那些Ctrl C+Ctrl V的人不要告诉他们,让他们直接粘上面的(滑稽)
这个办法呢,其实就是这样的:
如果剩下的未输入的数字比一半多,就说明肯定至少要转一圈队列。那么其实只要转 要转的次数%剩下的个数 次,时间复杂度大大缩减了。

另外,如果上面的取余是0的话,就往前找一个。

赋值的话不用再定义一个bool数组,原来数组初始值0就可以了。

我要贴代码了,Ctrl C+Ctrl V的人看这里:

#include
using namespace std;
int a[10010],j=1,n;
int main(){
	scanf("%d",&n);
	a[1]=1;//第一个数的赋值
	for(int i=2;i<=n;i++){
		int p=i;//用p来代替要数的次数
		if(i>n/2&&i<n)p=(i+1)%(n-i+1)-1;
			if(p!=-1)for(int k=0;k<=p;k++){
			//因为是要隔p个,所以要多算一次(不是猪!)
			do{
				j++;
				if(j>n)j=1;//轮转队列开头
			}while(a[j]!=0);//终止情况,再往后找
		}
		if(p!=-1)a[j]=i;
		//p=-1就是余数为零的情况,余数不为零我就赋值
		if(p==-1)do{
				j--;
				if(j<1)j=n;
			}while(a[j]!=0);
			a[j]=i;//余数为0的操作
	}
	for(int i=1;i<=n;i++)printf("%d ",a[i]);
}

如果能帮到你,我很受荣幸!谢谢!
也许我的代码不是最优,还请大神指导!

你可能感兴趣的:(魔法师与扑克牌游戏 C++队列题目)