AcWing309 装饰围栏(计数dp+试填法)

我们想要求某一个真正的序列,很多情况下都是用试填法来求取,填一个数上去看看是否满足条件。

所以我们要知道以如果填当前数,那么以当前数为首的剩下的数和c的关系。所以我们知道要预处理出所有满足条件的数的个数,也就是计数问题。

很自然的,前两位要设计为i个数,第一位,也就是最左边这位填的数是第j大,为什么会想到设计最左边,因为我们肯定是从高位开始填,才能每次判断是否已经大于c了。

我们设计相对位置,因为题目的要求也是相对的大小。

继而发现信息还不够,因为题目要求高低的关系,所以再多加一维表示,该位是高位还是低位,高位就从i-1的低位转移,反之同理。

现在考虑转移方程,f[i][j][1]=sum(f[i-1][k][0])这里的k一定要小于j,因为i是高位,反之同理。

之后就是试填法计算是哪个数,首先要特判第一位,因为第一位是高位还是低位没定,后面的都是相对前一位的状态

显然先枚举高位,因为这样的数比较小

还要从小到大枚举x,表示填的数是第几大。因此要st数组记录数有没有被用过。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll f[21][21][2];
int st[21];
int a[21];
int n;
void init(){
    int i,j;
    f[1][1][0]=f[1][1][1]=1;
    int k;
    for(i=2;i<=20;i++){
        for(j=1;j<=i;j++){
            for(k=j;k<i;k++)
            f[i][j][0]+=f[i-1][k][1];
            for(k=1;k<j;k++)
            f[i][j][1]+=f[i-1][k][0];
        }
    }
}
int main(){
    int t;
    cin>>t;
    init();
    while(t--){
        ll c;
        cin>>n>>c;
        int i,j;
        int k=0;
        memset(st,0,sizeof st);
        for(i=1;i<=n;i++){
            if(f[n][i][1]>=c){
                a[1]=i;
                st[i]=1;
                k=1;
                break;
            } 
            else{
                c-=f[n][i][1];
            }
            if(f[n][i][0]>=c){
                a[1]=i;
                st[i]=1;
                k=0;
                break;
            }
            else{
                c-=f[n][i][0];
            }
        }
        for(i=2;i<=n;i++){
            k^=1;
            int cnt=1;
            for(j=1;j<=n;j++){
                if(st[j])
                continue;
                if(k==1&&j>a[i-1]||k==0&&j<a[i-1]){
                    if(f[n-i+1][cnt][k]>=c){
                        st[j]=1;
                        a[i]=j;
                        break;
                    }
                    else
                        c-=f[n-i+1][cnt][k];
                }
                cnt++;
            }
        }
        for(i=1;i<=n;i++){
            cout<<a[i]<<" ";
        }
        cout<<endl;
    }
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/12715303.html