CSP-S 2020模擬訓練題1-信友隊T2 挑戰NPC
阿新 • • 發佈:2020-11-04
題意簡述
有一個\(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; }