CSP-S 2020模拟训练题1-信友队T2 挑战NPC
题意简述
有一个\(k\)维空间,每维的跨度为\(L\),即每一维的坐标只能是\(0,1, \cdots ,L-1\)。每一步你可以移动到任意一个曼哈顿距离到自己小于等于\(d\)的任意一个合法坐标。求一条空间中合法的哈密顿路。即,找一条经过且仅经过空间中每一个点一次的路径。
子任务编号 | 分值 | k= | L= | d= |
---|---|---|---|---|
1 | 1 | 1 | 1 | 1 |
2 | 9 | 2 | 3 | 1 |
3 | 20 | 10 | 2 | 1 |
4 | 20 | 3 | 3 | 1 |
5 | 20 | 10 | 3 | 2 |
6 | 30 | 10 | 3 | 1 |
分析
其实这道题就是csp 2019-s的T1格雷码的扩展版。格雷码有一个性质:同维相邻格雷码只有一个维度上的值发生了改变,且改变的值仅仅为1。
这告诉我们,d是多少并不重要,每次仅走一步也可以有哈密顿路。
当然,这是std的做法。我的做法是这样的:
-
从2维开始推。显然对于一个平面,绕s形走即可完全走完这个平面。这里发现最低维在0L-1、L-10之间鬼畜。
-
对于3维的空间,走完一个平面之后再将第一维+1(高度加一),就可以完成走完整个路径。
-
......
所以,高维每增加一,下面的维度就要来一遍循环。
那么如何决定循环的起点、终点和顺序(增还是减)呢?发现只有更高维改变了1,这个循环的顺序才会发生改变。
那么对这一位的更高维求和,判断其奇偶性,奇偶性就代表着它的顺序了。
反正是构造题,构造的方法有无数种。只要有道理即可(
Code
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define re register
#define debug printf("Now is %d\n",__LINE__);
using namespace std;
template<class T>inline void read(T&x)
{
char ch=getchar();
while(!isdigit(ch))ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
}
inline int read()
{
re int x=0;
re char ch=getchar();
while(!isdigit(ch))ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
int G[55];
template<class T>inline void write(T x)
{
int g=0;
do{G[++g]=x%10;x/=10;}while(x);
for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar(' ');
}
int k,L,d;
vector<int>now;
IL int judge(int w)
{
int ans=0;
for(int i=0;i<w;i++) ans+=now[i];
if(ans&1) return -1;
return 1;
}
IL int limit(int w)
{
if(judge(w)==1) return L-1;
return 0;
}
int main()
{
k=read();
L=read();
int tot=1;
for(int i=1;i<=k;i++) tot*=L,now.push_back(0);
if(k==1&&L==1&&d==1)
{
cout<<0<<endl;
return 0;
}
if(k==2&&L==3&&d==1)
{
cout<<"0 0\n0 1\n0 2\n1 2\n1 1\n1 0\n2 0\n2 1\n2 2"<<endl;
return 0;
}
if(k==3&&L==3&&d==1)
{
cout<<
"0 0 0\n0 0 1\n0 0 2\n0 1 2\n0 1 1\n0 1 0\n0 2 0\n0 2 1\n0 2 2\n"
"1 2 2\n1 2 1\n1 2 0\n1 1 0\n1 1 1\n1 1 2\n1 0 2\n1 0 1\n1 0 0\n"
"2 0 0\n2 0 1\n2 0 2\n2 1 2\n2 1 1\n2 1 0\n2 2 0\n2 2 1\n2 2 2"<<endl;
return 0;
}
for(re int i=1;i<=tot;i++)
{
for(re int l=0;l<k;l++) write(now[l]);
puts("");
for(re int j=k-1;j>=0;j--)
{
if(now[j]!=limit(j))
{
now[j]+=judge(j);
break;
}
}
}
return 0;
}