http://acm.hust.edu.cn/vjudge/problem/51198
题意:多组数据,n = 9,m = 4。相当于n堆纸牌,m = 4位每堆纸牌的最上面。每次从所有纸牌的最上面,取前一个字符相当的两张牌,求最后都能取完的概率是多少。
解析:直接暴搜,如果都是相等的,那就相当于是暴搜2^36次,肯定是不对的。记录所有纸牌,每堆纸牌的个数,作为状态。当前状态dp[i]等于所有子状态的概率,除以子状态的个数(根据全概率公式可得)。可以利用dp[][][][][][]开个九维的。也可以等效为每次乘以五后得到的整数。注意tnum记录的是纸牌的个数,如果是纸牌的位置会出现-1的情况。
写代码的时候注意dfs的返回值是double,不能用int接受
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
char s[10][10][3];
bool vis[29531250];
double dp[29531250];
int n = 9,m = 4;
bool match(char a[],char b[])
{
if(a[0] == b[0])
return true;
return false;
}
double dfs(long long ans)
{
if(ans== 0)
return 1;
if(vis[ans] == true)
return dp[ans];
vis[ans] = true;
bool flag = false;
int tnum[10];
int tans = ans;
for(int i = 0; i < n; i ++)
{
tnum[i] = tans % (m + 1);
tans = tans / (m + 1);
if(tnum[i] != 0)
{
flag = true;
}
}
if(flag == false)
{
//cout<<"get"<<endl;
return 1.0;
}
double sum = 0,win = 0;
long long temp;
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < i ; j ++)
{
if(tnum[i] >= 1 && tnum[j] >= 1 &&match(s[i][tnum[i] - 1],s[j][tnum[j] - 1]))
{
sum ++;
tnum[i] --;
tnum[j] --;
temp = 0;
for(int k = n - 1; k >= 0; k --)
{
temp = temp * (m + 1) + tnum[k];
}
//cout<<temp<<endl;
dp[ans] +=dfs(temp);
win += dfs(temp);
tnum[i] ++;
tnum[j] ++;
}
}
}
vis[ans] = true;
if(sum == 0)
{
dp[ans] = 0;
return 0;
}
dp[ans] = win * 1.0 / sum;
return dp[ans];
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%s",&s[0][0]) != EOF)
{
memset(vis,false,sizeof(vis));
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < m; j ++)
{
if(i == 0 && j == 0)
continue;
scanf("%s",&s[i][j]);
}
}
/* for(int i = 0; i < n; i ++)
{
for(int j = 0; j < m; j ++)
cout<<s[i][j]<< " ";
cout<<endl;
}*/
long long ans = 1;
for(int i = 0; i < n; i ++)
ans = ans *( m + 1) + m;
printf("%f\n",dfs(ans));
}
return 0;
}