树型dp poj3071 Football

因为是二叉结构,,所以会比较容易想到树型的结构,所以自然想到树型dp

然而怎么设dp呢,,我是这样的

dp[MX<<2][MX],第一个表示二叉树的节点编号,第二个表示i在这个节点所对应的区间中赢到最后的概率

节点对应的区间,和线段树的写法是一样的,,其实就是在线段树的build的代码上,把push_up改了一下而已,其他的都差不多


对于某个节点,先求出左右节点的概率情况,然后把左右节点的答案合并

在节点对应的区间内,枚举i赢了

如果i在左树,那么就枚举右树中有哪些人j。然后积累i赢所有的j的概率,最后乘上i在左树中赢到最后的概率

这样就把左右的答案合并了,然后再回溯到父节点继续就可以了


说起来很玄,,其实代码很简单...

#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<functional>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 150 + 5;
const int INF = 0x3f3f3f3f;
#define For(i,x,y) for(int i=x;i<=y;i++)
#define Mem(x,y) memset(x,y,sizeof(x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 1,n,1

int n;
double A[MX][MX];
double dp[MX << 2][MX];

void solve(int l, int r, int rt) {
    if(l == r) {
        dp[rt][l] = 1;
        return;
    }

    int m = (l + r) >> 1;
    solve(lson);
    solve(rson);
    For(x, l, r) {
        if(x <= m) {
            For(i, m + 1, r) {
                dp[rt][x] += dp[rt << 1 | 1][i] * A[x][i];
            }
            dp[rt][x] *= dp[rt << 1][x];
        } else {
            For(i, l, m) {
                dp[rt][x] += dp[rt << 1][i] * A[x][i];
            }
            dp[rt][x] *= dp[rt << 1 | 1][x];
        }
    }
}

int main() {
    //freopen("input.txt", "r", stdin);
    int n;
    while(~scanf("%d", &n), n >= 0) {
        Mem(dp, 0);

        n = 1 << n;
        For(i, 1, n) {
            For(j, 1, n) {
                scanf("%lf", &A[i][j]);
            }
        }

        solve(root);
        double Max = 0;
        int ans;
        For(i, 1, n) {
            if(dp[1][i] > Max) {
                Max = dp[1][i];
                ans = i;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}


你可能感兴趣的:(树型dp poj3071 Football)