题干:
杀人狂魔汉尼拔博士逃狱了。通缉令发布后,大量军警出动并实施全天候追捕,不过狡猾的汉尼拔博士并没有落网。过了d日后,束手无策的警察们拜访了有着“编程天才”之称的查理教授。查理教授对汉尼拔博士留在监狱的笔记本进行分析后,做出了如下假设。
- )汉尼拔博士为了避开检查,只走山路;
- )汉尼拔博士越狱当天选择了与监狱相邻的村子之一作为藏身之处;
- )汉尼拔博士为了逃避追捕,每天往一个相邻的村子逃窜。
为了验证假设,教授找到了与监狱所在村子以山路连接的n个村子的地图。汉尼拔博士会按照此假设行动,而且会随机选择一个备选的村子。编写程序计算d日后汉尼拔博士在各个村子的概率。
例如监狱在第三个村子,逃狱后的汉尼拔博士会在0、1、2、4、5中任意选择一个村子藏身。因此,1天后汉尼拔博士藏在第0号村子的概率是1/5,两天后藏在第1号村子的概率是1/15。
输入:
第一行输入测试用例的个数C(1≤C≤50)。之后各行输入地图上显示的村子个数 N(2≤N≤50)和逃狱后经过的天数D(1≤D≤100),以及监狱所在村子的号码P(0≤P<N),村子的号码由0到N-1的数字组成。之后N行里各输入N个整数,形成一个序列A。第i行j列的数值A[i][j]如果等于1,就表示从第i号村子到第j号村子有山路可走;如果是0,则表示无路可通。接下来的一行输入要计算概率的村子的个数T(0≤T<N),最后一行以整数型输入要计算概率的村子的号码Q(0≤Q<N)。
如果一个村子与另一个村子相连,那么相反的路径也必定存在。可假设一个村子连接到自身的路径不存在。
输出:
每个测试用例以T个实数输出汉尼拔博士可能藏匿的概率。存在小于10-7的绝对/相对误差的答案将被视为正确答案。
示例输入值:
2
5 2 0
0 1 1 1 0
1 0 0 0 1
1 0 0 0 0
1 0 0 0 0
0 1 0 0 0
3
0 2 4
8 2 3
0 1 1 1 0 0 0 0
1 0 0 1 0 0 0 0
1 0 0 1 0 0 0 0
1 1 1 0 1 1 0 0
0 0 0 1 0 0 1 1
0 0 0 1 0 0 0 1
0 0 0 0 1 0 0 0
0 0 0 0 1 1 0 0
4
3 1 2 6
示例输出值:
0.83333333 0.00000000 0.16666667
0.43333333 0.06666667 0.06666667 0.06666667
分析:
——我懒得写了,代码有注释。
代码:
#include <iostream>
using namespace std;
int C; //使用次数
int N; //村子个数
int D; //逃跑天数
int p; //监狱所在地
int T; //计算的村子
int Q[50]; //需要计算的村子(多个)
double A[50][50] = {}; //每个村子的联通情况
double B[100][50] = {}; //用于计算储存概率---100天 50个村
double S[50] = {}; //用于储存每个村子有多少联通村
double R[50][50] = {}; //用于储存结果
void ZxxInput() {
cout << "请输入村子个数、逃跑天数、监狱所在地";
cin >> N>>D>>p; //输入所需数据
B[0][p] = 1; //第0天在p村的概率为1
cout << "输入每两个村子是否联通"<<endl;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
cin >> A[i][j];
S[i] = S[i] + A[i][j]; //计算每个村子有多少联通村
}
}
cout << "输入要计算几个村子";
cin >> T;
cout << "输入T个村子的编号";
for(int i=0;i<T;i++){
cin >> Q[i];
}
}
void ZxxDeal() { //概率运算处理
cout << "开始运算处理" << endl;
for (int i = 1; i <= D; i++) { //计算每一天的结果
for (int j = 0; j < N; j++) { //计算每一村的结果
for (int k = 0; k < N; k++) { //第i天,j个村子的结果
遍历k个村子
if (S[k] != 0) {
B[i][j] = (B[i - 1][k] * (1 / S[k]) * A[j][k]) + B[i][j];
}
else
{
continue;
}
}
}
}
}
void ZxxARM(int n) { //多次的结果储存
for (int i = 0; i < T; i++) {
R[n][i] = B[D][Q[i]];
}
cout << endl;
}
void ZxxPrint() { //输出最后结果
for (int i = 0; i <C; i++) {
for (int j = 0; j < 50; j++) {
if (R[i][j] <0) {
break;
}
cout << R[i][j] << " ";
}cout << endl;
}
}
void ZxxDie() { //每次都需要将数组归零
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 50; j++) {
B[i][j] = 0;
S[j] = 0;
}
}
}
void ZxxDieR() { //结果数组的初始化
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 50; j++) {
R[i][j] = -1;
}
}
}
int main()
{
cout << "输入模拟次数";
cin >> C;
ZxxDieR();
for(int i=0;i<C;i++){
ZxxInput();
ZxxDeal();
ZxxARM(i);
ZxxDie();
}
ZxxPrint();
return 0;
}
好吧还是写点分析吧。。。
首先分析问题的解法。这个代码每次的运算都会算出博士在某个村子的概率。然后下一天的概率是由前一天的概率得出。
那么这一天的概率如何得出:首先设博士在M村,这是第0天,在这个村子的概率为1;假设与M村相连的有K个村子,那么第一天,博士在这K个村子之一的概率为(1/K)乘1, 这里为什么要乘1,因为这个结果的上一天的概率为1。则存储第一天的情况为这K个村子、每个村子的概率为1/K。同理,第二天的情况某个村子(假设这个村子的前一天村子连接了N个村子)的情况应该是(1/K)乘(1/N)。。。。。。。
以此类推。
吐槽一点:代码中的ZxxARM()方法纯粹是为了迎合输入输出方式。(多次输入,一次输出) 另外因为这样,就需要在每次的运算后归零输入矩阵(InPut()方法),不然上次的输出会参与进这次的运算。