[ACM]CCF CSP [201612-5]E题 卡牌游戏【75分的程序】

博客介绍了如何利用马尔可夫过程解决CCF CSP 201612-5 E题的卡牌游戏问题。通过建立概率转移方程组,并尝试使用高斯消元法求解。然而,由于方程数量巨大(2^n-2个),在n最大为15的情况下,高斯消元法效率过低,不适合处理极限数据。文章探讨了方程组的稀疏性,但未提出具体的优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路:马尔可夫过程,转为求解方程组:每一种状态有一个概率,把概率转移方程列出来,用高斯消元法求解。

由于一共有2^n-2个方程,n最大为15,高斯消元法解方程是三次方的,无法通过极限数据。方程组是稀疏的,不知道要怎么优化。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<math.h>
#include<vector>
#include<iostream>
using namespace std;
#define N 15
#define pw(x) (1<<x)
int n,m;
double a[1<<N][N+1];  //
double p[N+1][N+1];
vector<int> qry;
int read(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<n;i++)
  for (int j=i+1;j<=n;j++){
    scanf("%lf",&p[i][j]);
    p[j][i]=1.0-p[i][j];
  }
  for (int i=1;i<=m;i++){
    int S=0;
    for (int j=1;j<=n;j++){
      int k;
      scanf("%d",&k);
      if(k)S+=pw(j-1);
    }
      qry.push_back(S);
  }
  return 0;
}
#define OPPSTATE(S)  (((1<<n)-1)^S)    //对手状态
#define CONTAIN(S,c) ((S&(1<<(c-1)))>0) //S状态是否包含卡牌c
#define FORSTATE(S,c) for (int c=1;c<=n;c++)if (CONTAIN(S,c))  //枚举S中的卡牌
double p1[N];
double p2[N];
double sum[N];
void calcP1P2(int S,double* p1){ //计算在状态S时,出每张牌的概率
  int S2=OPPSTATE(S);
  for (int i=1;i<=n;i++) p1[i]=sum[i]=0.0;
  double ssum=0;
  FORSTATE(S,c1){
   FORSTATE(S2,c2){
    sum[c1]+=p[c1][c2];
   }
   ssum+=sum[c1];
  }
  FORSTATE(S,c1) p1[c1]=sum[c1]/ssum;
}
void calcA(){                      //计算两两状态转移概率
  memset(a,0,sizeof(a));
  for (int S=1;S<(1<<n)-1;S++){    //状态0和(1<<n)-1为最终状态,不需要转移
    int S2=OPPSTATE(S);
    calcP1P2(S,p1);
    calcP1P2(S2,p2);
    //S 和 S2博弈
    FORSTATE(S,c1)
    FORSTATE(S2,c2){  //S 出c1,概率p1[c1],S2出c2概率p2[c2]
      //S赢,得到c2
      double prob_c1_win=p[c1][c2];
      a[S][c2]+=p1[c1]*p2[c2]*prob_c1_win;
      //S输,失去c1
      a[S][c1]+=p1[c1]*p2[c2]*(1-prob_c1_win);
    }
    for (int i=1;i<=n;i++){
      //printf("%d->%d %.2lf\n",S,S^pw(i-1),a[S][i]);
    }
  }

}
double mat[5000][5000];
void gauss(){//2^n-2个方程
  memset(mat,0,sizeof(mat));
  int sz=(1<<n)-2;
  for (int S=1;S<=sz;S++){
    for (int i=1;i<=n;i++){
      int S2=S^pw(i-1);
      if (S2==sz+1) mat[S][S2]=-a[S][i];
        else
         mat[S][S2]=a[S][i];
    }
    mat[S][S]=-1.0;
  }

  //for (int i=1;i<=sz;i++,putchar('\n'))
  //for (int j=1;j<=sz+1;j++)printf("%.2lf ",mat[i][j]);
  //
  for (int i=1;i<=sz;i++){
    int r=i;
    for (int j=i+1;j<=sz;j++){
      if (fabs(mat[j][i]) > fabs(mat[r][i])) r=j;
    }
    if (r!=i) for (int j=1;j<=sz+1;j++) swap(mat[i][j],mat[r][j]);
    for (int j=1;j<=sz;j++)
    if (j!=i){
      double f=mat[j][i]/mat[i][i];
      for (int k=i;k<=sz+1;k++) mat[j][k]-=f*mat[i][k];
    }
  }

  //printf("\nafter gausss\n");for (int i=1;i<=sz;i++,putchar('\n'))
  //for (int j=1;j<=sz+1;j++)printf("%.2lf ",mat[i][j]);
  for (int i=1;i<=sz;i++){
       mat[i][sz+1]/=mat[i][i];
      // printf("STATE[%d] %.2lf\n",i,mat[i][sz+1]);
  }
  mat[0][sz+1]=0.0;
  mat[sz+1][sz+1]=1.0;
}
int main(){
  read();
  calcA();
  gauss();
  for (int i=0;i<qry.size();i++){

    printf("%.5lf\n",mat[qry[i]][(1<<n)-1]);
  }
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值