CodeForces 107C

数位dp+有向图状压dp
数位dp:
枚举字典序,判断总情况数是否>=y
状压dp:
dp[i]代表座位集合i所有摆放情况数

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
typedef __int64 LL;

const int N=16;
vector<int> vv[N];
#define pb push_back
int getcount(int mask){
    int cnt=0;
    while(mask){
        cnt++;
        mask-=mask&-mask;
    }
    return cnt;
}
int check(int u,int mask){
    for(int i=0;i<vv[u].size();i++){
        int v=vv[u][i];
        if(mask>>v&1)return 0;
    }
    return 1;
}

int vis[N];
int sta[N];
LL dp[1<<N];
int s[N];
int n,m;LL Y;

LL getdp(int id,LL num){
    int ed=1<<n;
    memset(dp,0,sizeof(dp));
    for(int i=0;i<n;i++){
        s[i]=1<<i;
    }
    dp[0]=1;
    for(int i=0;i<ed;i++){
        for(int j=0;j<n;j++)if((i>>j&1)==0&&check(j,i)){
            if(j>id){
                dp[i|s[j]]+=dp[i];
                if(dp[i|s[j]]>num)dp[i|s[j]]=num;
            }
            else if(getcount(i|s[j])==sta[j]+1){
                dp[i|s[j]]+=dp[i];
                if(dp[i|s[j]]>num)dp[i|s[j]]=num;
            }
        }
    }
    //for(int i=0;i<=id;i++)printf("%d ",sta[i]);printf(":%I64d\n",dp[ed-1]);
    return dp[ed-1];
}

int dfs(int id,LL num){
    if(id==n){
        if(num==1)return 1;
        else return 0;
    }
    for(int i=0;i<n;i++)if(!vis[i]){
        sta[id]=i;
        vis[i]=1;
        LL tmp=getdp(id,num);
        if(tmp>=num){
            return dfs(id+1,num);
        }
        else {
            num-=tmp;
        }
        vis[i]=0;
    }
    return 0;
}

int main(){
    #ifdef DouBi
    freopen("in.cpp","r",stdin);
    #endif // DouBi

    while(scanf("%d%I64d%d",&n,&Y,&m)!=EOF){
        Y-=2000;
        for(int i=0;i<n;i++)vv[i].clear();
        for(int i=0;i<m;i++){
            int a,b;scanf("%d%d",&a,&b);a--;b--;
            vv[a].pb(b);
        }

        memset(vis,0,sizeof(vis));
        if(dfs(0,Y)){
            for(int i=0;i<n;i++){
                printf("%d%c",sta[i]+1,i==n-1?'\n':' ');
            }
        }
        else {
            printf("The times have changed\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值