2 条题解

  • 1
    @ 2025-11-8 15:52:50

    记忆化搜索题,类似 DP 。

    形式化题意:给定 nn 个数,求满足任意两个相邻的数差值大于给定数值的排列方案数。

    看到排列一般会直接想到dfs,但 nn1616 的,dfs O(n!)O(n!)tle,显然过不了。但不难发现计算时会经常出现前面已经排好的方案不一样,但后面剩下还没排的数都一样的情况,此时就会出现大量重复计算。考虑到影响当前方案数的因素只有:已经排好了多少个、前面都有哪些参与了排列、当前排列的最后一个数是什么。因此记忆化搜索,用 dp[k][state][last]dp[k][state][last] 记录当前应该填第 kk 个数,已经填到了 statestate 的状态( statestate 在二进制下哪些位上是 11 就代表哪几个数已经用了)且最后一次填的下标为 lastlast 的方案数。那么就可以这样转移:

    $$dp[k][state][last]=\sum dp[k+1][state|(1<<(i-1)][i],(state>>(i-1)\text{and}1)==1 $$

    关于复杂度:最劣情况下,每一种 statestate 被访问一次,对于每一种 statestate ,每一个数字都有机会成为 lastlast (如果这个数字在 statestate 下),然而 statestate 确定了 kk 就确定了。因此 O(n2n)O(n·2^n) ,而且不容易跑满。

    最后不要忘记dfs的边界条件。 这道题就做完了。

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    inline int re(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')f=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*f;
    }
    const int N=17;
    int n,K;
    int h[N];
    int dp[N][(1<<N)][N];
    inline int dfs(int k,int state,int last){
    	if(k==n+1)return 1;
    	if(dp[k][state][last])return dp[k][state][last];
    	for(int i=1;i<=n;i++){
    		if((state>>(i-1))&1||abs(h[i]-h[last])<=K)continue;
    		dp[k][state][last]+=dfs(k+1,state|(1<<(i-1)),i);
    	}
    	return dp[k][state][last];
    }
    signed main(){
    	n=re(),K=re();
    	for(int i=1;i<=n;i++)h[i]=re();
    	h[0]=-K-1;
    	cout<<dfs(1,0,0);
    	return 0;
    }
    
    

    信息

    ID
    580
    时间
    1000ms
    内存
    256MiB
    难度
    5
    标签
    (无)
    递交数
    36
    已通过
    15
    上传者