九度OJ 1338:角斗士 (递归、DP)

时间限制:3 秒

内存限制:32 兆

特殊判题:

提交:213

解决:66

题目描述:
角斗士是古罗马奴隶社会的一种特殊身份的奴隶,他们的职责是在角斗场上进行殊死搏斗,为了人们提供野蛮的娱乐。他们的结局或是战死,或者由于表现突出赢得胜利而获得释放。
现在在角斗场里有N个待战的角斗士(1 <=N<=18),每天都将举行一场比赛,为了保持比赛的刺激性,每场比赛前才会在所有当前活着的角斗士之中随机选择两名进行上场拼杀。每场比赛的结束条件即为其中一名被杀死。当进行了N场比赛之后,最后存活的角斗士将被释放。而你将被赋予一个任务,计算出每名角斗士最终存活的概率。我们将提供角斗士之间对战获胜的概率。
输入:
测试数据包括多个,每个测试数据包含两部分
首先第一行将输入一个整数N,其中1 <= N <= 18,代表角斗士的个数。
接下来将是一个N * N大小的概率矩阵P,代表角斗士之间战斗的获胜概率,例如P[i][j]就代表角斗士i战胜j的概率;同样P[j][i]则代表角斗士j战胜i的概率。我们保证P[i][j] + P[j][i] = 1。同时我们规定当i等于j时,P[i][j] 为 0。
输出:
对于每个测试案例,输出一行,包含N个小数,中间用空格隔开,分别代表第0、1、…、N个角斗士存活下来的概率,每个小数精确到小数点后3位。
样例输入:
2
0 0.5
0.5 0
3
0 1 1
0 0 0.5
0 0.5 0
样例输出:
0.500 0.500
1.000 0.000 0.000

思路:

参考别人的思路写的。

状态压缩DP。
初看规模,n只有18,马上想到是dfs搜索或者是状态压缩dp。
令数组dp[x]表示x在二进制上为1那些人活着的概率,比如现在只有3个人,
那么dp[5]表示第一个人和第三个人活着的概率。
由于比赛是随即选择两个人,那么每场比赛的概率为1 / C(活着的人的个数,2)。
最终的概率应该是叠加上去。
那么状态转移方程为pos[i ^ (1 << j)] += pos[i] * win[j][k] / sum。


代码:

#include <stdio.h>
 
#define N 18
#define M (1<<N)
 
int survive(int k, int n)
{
    int i, count;
    count = 0;
    for (i=0; i<n; i++)
    {
        count += (k & 1);
        k >>= 1;
    }
    return count;
}
 
int main()
{
    int i, j, k, n;
    double P[N][N], S[M];
    while(scanf("%d", &n) != EOF)
    {
        for (i=0; i<n; i++)
        {
            for (j=0; j<n; j++)
            {
                scanf("%lf", &P[i][j]);
            }
        }
 
        for (k = (1<<n) - 1; k>0; k--)
            S[k] = 0;
        S[(1<<n) - 1] = 1;
        for (k = (1<<n) - 1; k>0; k--)
        {
            int count;
            count = survive(k, n);
            //printf("%d, %d\n", k, count);
            if (count == 1)
                continue;
            int num = count*(count-1)/2;
            for (j=0; j<n; j++)
            {
                int bitJ = 1<<j;
                if ((k & bitJ) == 0)
                    continue;
                int nk = (k ^ bitJ);
                //printf("k=%d, count=%d, nk=%d\n", k, count, nk);
                double tmpS = 0;
                for (i=0; i<n; i++)
                {
                    int bitM = 1<<i;
                    if ((nk & bitM) == 0)
                        continue;
                    //printf("i=%d, j=%d\n", i, j);
                    tmpS += P[i][j];
                }
                tmpS *= S[k];
                tmpS /= num;
                S[nk] += tmpS;
                //printf("S[%d]=%.3lf\n", nk, S[nk]);
            }
        }
 
        //for (k = (1<<n) - 1; k>0; k--)
        //  printf("%.3lf\n", S[k]);
        for (i=0; i<n-1; i++)
            printf("%.3lf ", S[1<<i]);
        printf("%.3lf\n", S[1<<i]);
    }
    return 0;
}
/**************************************************************
    Problem: 1338
    User: liangrx06
    Language: C
    Result: Accepted
    Time:980 ms
    Memory:2892 kb
****************************************************************/


posted on 2015-11-15 12:36  梁山伯  阅读(269)  评论(0编辑  收藏  举报

导航