1. 程式人生 > 實用技巧 >CSP-S 2020模擬訓練題1-信友隊T2 挑戰NPC

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形走即可完全走完這個平面。這裡發現最低維在0~L-1、L-1~0之間鬼畜。

  • 對於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;
}