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;
}
posted @ 2020-11-04 21:51  Vanilla_chan  阅读(214)  评论(0编辑  收藏  举报