SGU-448 Controlled Tournament ( 状态DP )

输入比赛人员个数 N 和你希望赢的人的编号 M,

然后输入 N * N 的输赢表,第 i 行 第 j  列为 1,代表 i 能赢 j。

求 M 最后能赢,且总比赛树的最小高度时,一共有多少种可能。

比如输入:

7 2

0 1 0 0 0 1 0

0 0 1 0 1 1 1

1 0 0 1 1 0 0

1 1 0 0 0 1 0

1 0 0 1 0 0 1

0 0 1 0 1 0 0

1 0 1 1 0 1 0

则输出:

139

#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;

vector< int > wins_table[16];
int DP[16][20][1 << 16];
int one_in_bits_[1 << 16];

int dfs_search( int root, int height, int set_bits ){

    if( one_in_bits_[set_bits] == 1 )
        return 1;

    if( ( 1 << height ) < one_in_bits_[set_bits] )
        return 0;

    if( DP[root][height][set_bits] != -1 )
        return DP[root][height][set_bits];
    else
        DP[root][height][set_bits] = 0;

    for( int set1 = set_bits & ( set_bits - 1 ); set1; set1 = set_bits & ( set1 - 1 ) ){
        if( ( set1 >> root ) & 1 ){
            int set2 = set1 ^ set_bits;
            for( int i = 0; i < wins_table[root].size(); ++i ){
                int other = wins_table[root][i];
                if( ( set2 >> other ) & 1 ){
                    DP[root][height][set_bits] += dfs_search( root, height - 1, set1 ) *
                                                  dfs_search( other, height - 1, set2 );
                }
            }
        }
    }

    return DP[root][height][set_bits];

}

int main(){

    for( int i = 0; i < ( 1 << 16 ) - 1; ++i )
        one_in_bits_[i] = one_in_bits_[i >> 1] + ( i & 1 );

    int players, winner;

    while( cin >> players >> winner ){

        memset( DP, -1, sizeof( DP ) );

        for( int i = 0; i < players; ++i ){
            wins_table[i].clear();
            for( int j = 0; j < players; ++j ){
                int win;
                cin >> win;
                if( win )
                    wins_table[i].push_back( j );
            }
        }

        int height = ceil( log( players ) / log( 2 ) );
        int ans = dfs_search( winner - 1, height, ( 1 << players ) - 1 );

        cout << ans << endl;

    }

    return 0;

}

思路:

DP[root][height][set] 代表 以 root 为根,比赛树高度为 height 时,已经比赛过人员集合为 set 时的可能性

则 DP[root][height][set] = ∑ ( DP[root][height - 1][set1] * DP[other][height - 1][set - set1] )

时间: 2024-10-18 16:16:50

SGU-448 Controlled Tournament ( 状态DP )的相关文章

SGU 131.Hardwood floor 状态压缩DP

#include <cstdio> #include <cstring> typedef __int64 LL; LL dp[10][1<<10]; int n, m; int st, flag; void dfs(int i, int s, int ns, int j, LL num) { if(j == m) { dp[i+1][ns] += num; return; } if((s&(1<<j)) == 0) { if(ns&(1<

hdu4800_Josephina and RPG(二维状态dp)

/////////////////////////////////////////////////////////////////////////////////////////////////////// 作者:tt2767 声明:本文遵循以下协议自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 查看本文更新与讨论请点击:http://blog.csdn.net/tt2767 链接被删请百度: CSDN tt2767 ///////////////

Missile:双状态DP

题目 描写叙述 Long , long ago ,country A invented a missile system to destroy the missiles from their enemy . That system can launch only one missile to destroy multiple missiles if the heights of all the missiles form a non-decrease sequence . But recentl

ZOJ 3802 Easy 2048 Again 状态DP

zoj 上次的月赛题,相当牛的题目啊,根本想不到是状态压缩好吧 有个预先要知道的,即500个16相加那也是不会超过8192,即,合并最多合并到4096,只有2的12次方 所以用状态压缩表示前面有的序列组合,找到了符合的,就往上累加合并生成新状态,否则就添加到前面的状态的后面构成新状态,因为每一个的状态都由前一个所得,用滚动数组即可 #include <iostream> #include <cstdio> #include <cstring> #include <

HDU 3001 Travelling 状态DP

TSP问题,不懂就是每个点最多访问两次,最少访问一次. 所以,我们可以用三进制来当做状态. 这个题练习了一下三进制--0.1.2 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 #include <algorithm> 7 #include <string> 8

hdu6006 Engineer Assignment 状态dp 定义dp[i][s]表示前i个工程状态为s可以执行的最大工程数。s表示前i个工人选走了s状态的工程师。

/** 题目:hdu6006 Engineer Assignment 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6006 题意:已知n个工程,每个需要某些领域的专家.有m个工程师,每个人擅长一些领域. 一个工程师只能参加一个工程.一个工程可以多个工程师参加. 如果参加某个工程的工程师他们擅长的领域覆盖了该工程需要的领域.那么该工程可以执行. 问最多可以执行多少个工程. 思路: 定义dp[i][s]表示前i个工程状态为s可以执行的最大工程数.s表示前

ZOJ-3777-Problem Arrangement(状态DP)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777 题意: 输入n和m,接下来一个n*n的矩阵,a[i][j]表示第i道题放在第j个顺序做可以加a[i][j]的分数,问做完n道题所得分数大于等于m的概率.用分数表示,分母为上述满足题意的方案数,分子是总的方案数,输出最简形式. 分析: 一开始用的DFS做的,复杂度太高,果断超时.. 正解是状压DP: 由于总的方案数为n! ,简化为求给一个n*n的矩阵,每一行每一列各选

SGU 310. Hippopotamus( 状压dp )

题目大意:给N块板, 有A,B2种类型的板, 要求任意M块连续的板中至少有K块B板.1≤n≤60,1≤m≤15,0≤k≤m≤n. dp(x, s)表示第x块板, x前M块板的状态为s, 然后合法状态转移就行了. --------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm>

点集配对问题(状态dp)

给定n个点(n是偶数)使得两个点两两配对,最后总的距离和最小. 用是表示集合,那么dp[s]表示集合s配对后的最小距离和  , 状态转换方程为  表示集合中任意拿两个元素配对,然后转移为更小的两个集合的点集配对.i=min(s)表示i为集合中的第一个元素,因为第一个元素肯定要配对的, 所以找到集合中的第一个元素后,我们枚举要配对的另外一个元素j,j>i && j属于s 我们用二进制表示一个集合,1表示该元素存在,0表示不存在,然后进行状态转移 我们不需要担心一个状态的子状态没有被算出