ZOJ 4103 浙江省第16届大学生程序设计竞赛 D题 Traveler 构造

这个题,正赛的时候也没有过,不过其实已经有了正确的解法,可惜时间不多了,就没有去尝试。

题意是有n个点,i点能通向i-1,然后i和i*2、i*2+1互通。

请你构造一种路径从1能走完所有点,并且不重复经过每个点。

 

一开始我先考虑了有什么方法能固定地走完所有点。

然后发现 1-(2)-(4)-3-(6)-5-(10)....()为跳跃

仅当走完了之前的所有点时才跳跃,跳跃完后再回退走完所有点。

这样走的话,是可以走完所有点的。

但是这样的限制是,我们只能在n=1/2/4/6/10...这些数时走完所有点,这个可行的集合可以用ai=(a(i-2)+1)*2来递推。

换句话说,我们只要求出数列a 就可以按这个数列走出方案。

这个题目i可以通向i*2,也可以通向i*2+1,所以我们得到了另一种移动方式ai=(a(i-2)+1)*2+1。

这样我们能走的数就多了许多(其实是全部数都能走完 233)

我们只要从ai=n 反向推这个数列前面的项,显然a(i-2)=ai/2-1或者(ai-1)/2-1

而且这个其实是固定的,奇数的话想/2一定要-1,偶数反之。

然后我们就求出了这个数列的ap ap-2 ap-4.....

数列的其他项其实不重要 只要满足ap>ap-1即可,可以随便构造(大概)。

以下为具体代码

#include<bits/stdc++.h>
using namespace std;
int i,i0,n,m,T;
bool vis[100005];
stack<int>stk;
vector<int>v;
int main()
{
    vis[0]=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)vis[i]=0;
        v.clear();
        while(1)
        {
            if(n<=1)break;
            stk.push(n);
            if(n%2)n=(n-1)/2-1;
            else n=n/2-1;
        }
        int now=1;
        while(1)
        {
            v.push_back(now);
            vis[now]=1;
            if(!vis[now-1])now--;
            else
            {
                if(stk.empty())break;
                else if(now*2==stk.top())
                {
                    now*=2;
                    stk.pop();
                }
                else if(now*2+1==stk.top())
                {
                    now=now*2+1;
                    stk.pop();
                }
                else
                {
                    if(!vis[now*2])now=now*2;
                    else now=now*2+1;
                }
                
            }
        }
        for(i=0;i<v.size();i++)printf("%d%c",v[i],i==v.size()-1?'\n':' ');
    }
    return 0;
}

 

posted @ 2019-04-30 09:28  BiteTheDDDDt  阅读(848)  评论(1编辑  收藏  举报