Gym - 100837 F Controlled Tournament(竞赛树 状态压缩)

题目链接:https://codeforces.com/gym/100837

 

题目大意:给出n个人之间的胜负关系,问能构造出多少种尽可能层数少的竞赛树使得m能够赢

 

题目思路:一个坑点在于正确理解层数少的含义,层数少就是说每一轮都没有人空闲都在比赛,那么很容易得出竞赛树的高度就等于ceil(log2(n)),参照二叉树的性质。然后由于这个很小的数据范围,很容易想到是通过状压PD获得最终结果,dp建三维,第一维是谁赢,第二维当前是第几层,第三维是当前参与比赛的人的集合。那么dp[u][h][s]就可以通过枚举s的各种情况获得,枚举得到i,然后要求u在i中,然后用s^i就能得到另一边的状态,观察另一边的状态中是否有人(k)能被打败,如果有的话,继续以k能被打败,h-1层,s^i的状态继续递归前进,最后得到最终答案。对于剪枝,一个是记忆化搜索 ,如果已经有结果了直接return,其次是如果当前状态只有一个人了,那么他一定是冠军,返回1,还有一个剪枝是1<<h表示当前层数最多的人数,如果状态表示的1的数量比1<<h要多,那么肯定不行直接return 0,这是一个很强的剪枝,也比较难想到

 

以下是代码:

#include<bits/stdc++.h>

using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 20+5;
int a[MAXN][MAXN],n,m,h,dp[MAXN][MAXN][1<<16],bits[1<<16];
int dfs(int u,int h,int s){
    if(dp[u][h][s]!=-1)return dp[u][h][s];
    if((1<<h)<bits[s])return 0;
    if(bits[s]==1)return 1;
    dp[u][h][s]=0;
    for(int i=s&(s-1);i;i=s&(i-1)){
        if(i&(1<<u)){
            int j=s^i;
            rep(k,0,n-1){
                if(a[u][k]&&((1<<k)&j)){
                    dp[u][h][s]+=dfs(u,h-1,i)*dfs(k,h-1,j);
                }
            }
        }
    }
    return dp[u][h][s];
}
int main()
{
    freopen("f.in","r",stdin);
	freopen("f.out","w",stdout);
    memset(bits,0,sizeof(bits));
    rep(i,1,(1<<16)-1){
        if(i&1)bits[i]=bits[i>>1]+1;
        else bits[i]=bits[i>>1];
    }
    scanf("%d%d",&n,&m);
    h=ceil(log2(n));
    rep(i,0,n-1){
        rep(j,0,n-1){
            scanf("%d",&a[i][j]);
        }
    }
    memset(dp,-1,sizeof(dp));
    int ans=dfs(m-1,h,(1<<n)-1);
    printf("%d\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值