poj-3071

22 篇文章 0 订阅
//528K  94MS    G++ 
#include <cstdio>
#include <cstring>
#include <cmath>

const int MAX = 130;

double P[MAX][MAX];

double DP[MAX][9];

int N;
int teamNum;

void getVSId(int curTeamId, int winTime, int & beginVSId, int & endVsId) {
    int teamMemberCapcity = pow(2, winTime);
    beginVSId = 1;
    endVsId = beginVSId + teamMemberCapcity - 1;
    while(1) {
        if (curTeamId >= beginVSId && curTeamId <= endVsId) {
            break;
        } else {
            beginVSId = endVsId + 1;
            endVsId = beginVSId + teamMemberCapcity - 1;
        }
    }
    int middleId = (beginVSId + endVsId)>>1;
    if (curTeamId <= middleId) {
        beginVSId = middleId+1;
    } else {
        endVsId = middleId;
    }
    // printf("getVSId %d %d %d %d\n", curTeamId, winTime, beginVSId, endVsId);
}

void beginDP() {
    for (int winTime = 1; winTime <= N; winTime++) {
        for (int teamId = 1; teamId <= teamNum; teamId++) {
            if (winTime == 1) {
                if (teamId%2) { // is odd, VS teamId +1
                    DP[teamId][1] = P[teamId][teamId+1];
                } else { // is even, VS teamId -1
                    DP[teamId][1] = P[teamId][teamId-1];
                }
                // printf("DP %d %d %lf\n", teamId, 1, DP[teamId][1]);
            } else {
                int beginVSId;
                int endVsId;
                getVSId(teamId, winTime, beginVSId, endVsId);
                double tmp = 0.0;

                for (int i = beginVSId; i <= endVsId; i++) {
                    tmp += DP[i][winTime-1]*P[teamId][i];
                    // // printf("%lf %lf %lf %lf\n",
                    //     // DP[i][winTime-1], P[teamId][i],
                    //     DP[i][winTime-1]*P[teamId][i], tmp);
                }
                DP[teamId][winTime] = tmp*DP[teamId][winTime-1];
                // printf("DP %d %d %lf %d %d\n", teamId, winTime, DP[teamId][winTime], beginVSId, endVsId);
            }
        }
    }
}

void getMostPossibleWinner() {
    beginDP();
    int winnerId = 0;
    double maxP = 0.0;
    for (int i = 1; i<= teamNum; i++) {
        if (maxP < DP[i][N]) {
            maxP = DP[i][N];
            winnerId = i;
        }
    }
    // printf("%d %lf\n", winnerId, maxP);
    printf("%d\n", winnerId);
}

int main() {
    while(scanf("%d", &N) != EOF) {
        if (N == -1) {
            return 0;
        }
        teamNum = pow(2, N);
        memset(P, 0, sizeof(P));
        memset(DP, 0, sizeof(DP));
        for (int i = 1; i <= teamNum; ++i) {
            for (int j = 1; j <= teamNum; ++j) {
                double Pij;
                scanf("%lf", &Pij);
                P[i][j] = Pij;
            }
        }
        getMostPossibleWinner();
    }
}

披着概率外衣的DP基础题,在写getVsID时犯了个低级错误.

题目咋一看比较唬人,不过结合着例子分析一下,DP的关系就出来了:

首先,题目指明了,比赛的对手安排:

1对2, 3对4 ...... N-1对N.

那么,对于1来说,赢一场的概率就是 P(1,2)(1击败2的概率)

而对于第二轮比赛,按照题目的规定,

1的选手只能是 3或者4,那么赢得概率就是 P(1,2)( P(1,3)*P(3,4) + P(1,4)*P(4,3) )

就这样递推,会发现,

某个选手i, 赢得N场比赛的概率DP[i][N]可以这样表示:

DP[i][N-1] (DP[A1][N-1]P(i,A1) + DP[A2][N-1]P(i,A3) + ... + DP[AN][N-1]P(i,AN))(A1... AN是 i在第N场比赛中可能遭遇的对手),

那么如何确定A1....AN, 可以观察发现:

对于1来说,第一场比赛的对手只可能是2,

第二场比赛的对手只可能是 3, 4

第三场比赛的对手只可能是 5 6 7 8

.......

可以发现,对于某个选手i来说,在第N轮比赛中,他是从一个规模为2的N-1次方的团体中脱颖而出的,而与之相对的也是一个2的N-1次方大小的团队,

这个团队的所有对手都可能会成为i 在第N轮比赛的对手。(对于该团队某个人 j, 能和i比赛的概率就是在此团队胜出的概率,就是DP[j][N-1])

getVsId就是求出与i在第N轮比赛中可能比赛的对手的序号范围,步骤也很简单,

对于第N论比赛,

i 脱颖而出的团队 和 可能做i对手的团队 会一起组成一个  连续   的 2的N次方大小的团队,

先确定这个团队T的范围,然后确定i是在T的前半部分(那么对手就在后半部分),还是后半部分(那么对手就在前半部分)。

最后利用上面的DP递推方程求出即可.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值