「zig-zag pattern」笔记
YangJZHello · · 算法·理论
zig-zag,意为『之字形』。zig-zag pattern主要解决在完全图上找到一条经过所有节点的简单路径的问题。其具体算法如下:
现有一个节点数
(
或
(
将所有节点看作是一个环,当遇到节点编号越界的情况,使其变为在环上对应的点即可。
如图:
应用
P9837 汪了个汪
在本题中,将:
- 所有由任意一行内相邻两个数组成的无序二元组互不相同
- 一行内的所有数必须互不相同
转换为将一个节点数为
考虑
根据高斯算法可知这
#define re register
#define rep(i, s, t) for(re int i=s; i<=t; ++i)
int lim(int x) {
if(x==0) return n;
else if(x>0&&x<=n) return x;
else if(x>n) return x%n;
else if(x==-n) return n;
else return x+n;
}
rep(i, 1, n>>1) {
mem[i][1] = i;
rep(j, 1, n>>1) {
mem[i][2*j] = lim(i-j);
mem[i][2*j+1] = lim(i+j);
}
rep(j, 1, n+1-i) {
mem[n+1-i][j] = mem[i][n+1-j];
}
}
rep(i, 1, n) {
rep(j, 1, i) {
std::cout << mem[i][j] << ' ';
}
std::cout << '\n';
}
当
int lim_(int x) {
if(x==n+1) return 0;
else if(x>=0&&x<=n) return x;
else if(x>n) return x%(n+1);
else if(x==-n) return 1;
else return x+n+1;
}
rep(i, 0, ((n+1)>>1)-1) {
int k;
if(i%2) k = lim_((i+1)>>1);
else k = lim_(-((i+1)>>1));
mem[i][1] = k;
rep(j, 1, (n+1)>>1) {
mem[i][2*j] = lim_(k-j);
mem[i][2*j+1] = lim_(k+j);
}
rep(j, 1, n-i) {
mem[n-i][j] = mem[i][n+2-j];
}
}
rep(i, 1, n) {
rep(j, 1, i) {
std::cout << mem[i][j] << ' ';
}
std::cout << '\n';
}
CF1290D Coffee Varieties (hard version)
在得出本题 easy version 的算法后,考虑用 zig-zag pattern 进行优化。将所有分块之间的两两比对转换为完全图中的边,使用 zig-zag pattern 将整个图划分为边不相交的链,再进行操作。
代码:
void better() {
if(k==1) {
rep(i, 1, n) {
rep(j, i+1, n) {
ask(i);
if(ask(j)=='Y') death[j] = 1;
std::cout << "R\n";
}
}
std::cout << "! " << n-death.count() << '\n';
return;
}
rep(i, 1, n/k) {
int d=0;
rep(j, 1, n/k) {
x = i+d;
rep(s, 1, k) {
if(!death[(lim(x)-1)*k+s])
if(ask((lim(x)-1)*k+s)=='Y') {
death[(lim(x)-1)*k+s] = 1;
}
}
if(d>=0) {
d = -d-1;
}
else {
d = -d;
}
}
std::cout << "R\n";
}
std::cout << "! " << n-(death.count()) << '\n';
return;
}