[Tkey] A decorative fence

还是看看简单而富有美感的爆搜吧
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define tests int cases;cin>>cases;while(cases--)
int n,l;
vector<int> e;bool vis[21];int cnt=0;
void dfs(int p){
	if(cnt==l) return;
	if(p>n){
		cnt++;
		if(cnt==l){
			for(int i:e) cout<<i<<" ";
			cout<<endl;
		}
		return;
	}
	for(int i=1;i<=n;++i){
		if(!vis[i]){
			if(e.size()<=1){
				e.push_back(i);vis[i]=1;
				dfs(p+1);
				e.pop_back();vis[i]=0;
			}
			else if((*(e.end()-1)>*(e.end()-2)&&*(e.end()-1)>i)||
			(*(e.end()-1)<*(e.end()-2)&&*(e.end()-1)<i)){
				e.push_back(i);vis[i]=1;
				dfs(p+1);
				e.pop_back();vis[i]=0;
			}
		}
	}
}
signed main(){
	tests{
		e.clear();cnt=0;
		cin>>n>>l;
		dfs(1);
	}
}

思路

这题爆搜肯定是不行,考虑一个事实:

假设 1xxxxRank=a 的一种方案,1yyyyRank=b 的另一种方案,而目标 Rank k 满足 akb,则有 Rank=k 的方案的首位一定是 1.

跟我们猜数是一样的. 假设有个数给你猜,114 小了,191 大了,那你肯定知道这个数最高位是什么了.

所以我们就开个数组来转移并维护这个 Rank 值.

注意到 Rank 并不是非常好维护,我们可以考虑维护每种情况的方案数,然后按字典序从小到大依次加起来,这样就是 Rank 值了.

f[i][j][k] 为放入前 i 块木板构成的栅栏,当第 i 块木板的 Rank=j 时的方案数. 注意到这样还是不好维护,因为要考虑是高低高还是低高低,那么再开一维 k 来表示这个. k=11 为高,反之亦然.

那么这个转移非常好写,也不是本题的难点.

{f[i][j][1]=1kj1f[i1][k][0]f[i][j][0]=jki1f[i1][k][1]

这里唯一需要注意的是求和的范围. 因为我们这个 k 指代的是 Rank=k,而且会涉及到选高的还是选低的的问题,也就有了 k 的范围的差异.

那么还很容易注意到,这个转移和 n,m 完全没有关系,所以从多测里提出来作为初始化.

然后就是按上面的思想来逼近我们要求的答案.

先来确定第一位吧,我们需要做的就是遍历每个 1in,只要有 j=1i(f[n][j][0]+f[n][j][1])>m,就能判定 j1 是我们要求的那个第一位.

很显然,当我们之前几位选过某个数字,那我们就不能再选了,因此在之后的几次逼近中,我们还需要判断当前 Rank 的板子是不是已经被使用过了,然后进行类似的判断即可.

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define speed ios::sync_with_stdio(false);
#define tests int cases;cin>>cases;while(cases--)
#define clear(i) memset((i),0,sizeof (i))
int f[21][21][2];   //now fences have n planks, and the leftest planks ranking j
			        //k=0 means leftest is shorter,else taller
int n,m;
bool vis[21];
/*
f[i][j][1]=sum k{from 1 to j-1} f[i-1][k][0]
f[i][j][0]=sum k{from j to i-1} f[i-1][k][1]
*/
void prework(){
	f[1][1][1]=1;f[1][1][0]=1;
	for(int i=2;i<=20;++i){
		for(int j=1;j<=i;++j){
			for(int k=1;k<=j-1;++k){
				f[i][j][1]+=f[i-1][k][0];
			}
			for(int k=j;k<=i-1;++k){
				f[i][j][0]+=f[i-1][k][1];
			}
		}
	}
}
signed main(){
	prework();
	speed tests{
		cin>>n>>m;
		clear(vis);
		int now,last;
		for(int i=1;i<=n;++i){
			if(f[n][i][1]>=m){
				last=i;now=1;break;
			}
			else{
				m-=f[n][i][1];
			}
			if(f[n][i][0]>=m){
				last=i;now=0;break;
			}
			else{
				m-=f[n][i][0];
			}
		}
		cout<<last<<" ";
		vis[last]=true;
		for(int i=2;i<=n;++i){
			now=1-now;int rank=0;
			for(int len=1;len<=n;++len){
				if(vis[len]) continue;
				rank++;
				if((now==0 and len<last)or(now==1 and len>last)){
					if(f[n-i+1][rank][now]>=m){
						last=len;break;
					}
					else{
						m-=f[n-i+1][rank][now];
					}
				}
			}
			vis[last]=true;
			cout<<last<<" ";
		}
		cout<<endl;
	}
}
posted @   HaneDaniko  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示