您的位置:首页 > 其它

关于HDOJ的2901题的思路以及遇到的问题

2013-04-09 22:36 337 查看
最近想提高一下编程能力,参加了腾讯编程马拉松,结果五道题却制作出来了一道,有点受打击,于是就在HDOJ上找题练一下,随便就找到了HDOJ2901,于是开始了此文。(一开始就做这个似乎有点难的题,我开始怀疑自己是否有点好高骛远了)

题目就不多说了,见http://acm.hdu.edu.cn/showproblem.php?pid=2901

大意是要从接收到的字符串中找出最可能的发送字符串,已知的是发送a/b接受a/b的概率矩阵和发送a/b再次发送a/b的概率



题目的举例就是以此列举所有的可能性,然后比较每一种可能性的概率,取最大者。

所以一开始,我的思路很单纯:收发有a种字符,接受字符串长度w最大为300,所以最终的可能性有aw种可能性(其实对于题目的极限来说30300一定是难以承受的,也不知道最终会怎么去判断是否AC),而我就开始很单纯地取用pow(a,w)函数来作为循环结束的条件,结果总是超时。

我最开始的代码如下:

#include <fstream>

#include <iostream>

#include <vector>

#include <string>

#include <cmath>

#include <map>

//#include <algorithm>

//#define DEBUG

using namespace std;

int main(int argc, int * argv[])

{

//fstream cin("aaa.txt");

int n,a,i,j,num_case;

char c;

float d;

string s;

map<char,int> map_ch;

cin>>n;

while(n--)

{

cin >> a; //读入字符种类数

char ch[30];//vector<char> ch(a);

for(i=0;i<a;i++)

{

cin>>c;

ch[i] = c;

map_ch[c] = i;

//cout<<ch[i]<<endl;

}

//读入接受错误概率和字符继承概率矩阵

//注意下面这一行:vector<int后两个">"之间要有空格!否则会被认为是重载">>"。

float REP[30][30];//vector<vector<float> > REP(a, vector<float>(a)); //Receiving Error Probabilities

for(i=0;i<a;i++)

for(j=0;j<a;j++)

{

cin>>d;

REP[i][j] = d;

//cout<<REP[i][j]<<" ";

}

float CSP[30][30];//vector<vector<float> > CSP(a, vector<float>(a)); //Character Succession Probabilities

for(i=0;i<a;i++)

for(j=0;j<a;j++)

{

cin>>d;

CSP[i][j] = d;

//cout<<CSP[i][j]<<" ";

}

//读入测试案例

cin>>num_case;

for(j=0;j<num_case;j++)

{

cin>>s;

int s_length = s.length();

// 把输入的数据转换成0~a-1 比如ab转换成01 bb转换成11

vector<int> s_a(s_length);

for(i=0;i<s_length;i++)

for(int k=0;k<a;k++)

if(ch[k] == s[i]) s_a[i] =k;

//s_a[i] = map_ch[s[i]];

#ifdef DEBUG

cout<<"s_a : ";

for(i=0;i<s_length;i++)

cout<<s_a[i];

cout<<endl;

#endif

float max_p = 0.0; //最大概率

vector<int>MaxLikely(s_length); //最终结果

int max_maybes = pow(a,s_length);

for(i=0;i<max_maybes;i++) //一共有pow(a,s_length)种可能

{

vector<int>s_maybe(s_length); //可能的字符串

int n_maybes = i;

for(intk=0;k<s_length;k++) //将i转化成a进制数存在s_maybe中

{

s_maybe[k] = n_maybes % a;

n_maybes /= a;

}

//reverse(s_maybe.begin(),s_maybe.end());

#ifdef DEBUG

cout<<"s_maybe : ";

for(k=0;k<s_length;k++)

cout<<s_maybe[k];

cout<<endl;

#endif

// 计算概率

float liklyhood = 1;

for(k=0;k<s_length;k++)

{

liklyhood *=REP[s_maybe[k]][s_a[k]];

#ifdef DEBUG

cout<<"REP:"<<s_maybe[k]<<s_a[k]<<""<<endl;

#endif

}

for(k=0;k<s_length-1;k++)

{

liklyhood *=CSP[s_maybe[k]][s_maybe[k+1]];

#ifdef DEBUG

cout<<"CSP:"<<s_maybe[k]<<s_a[k]<<""<<endl;

#endif

}

#ifdef DEBUG

cout<<"likelyhood ="<<liklyhood<<endl;

#endif

if(liklyhood > max_p)

{

max_p = liklyhood;

for(k=0;k<s_length;k++)

MaxLikely[k] = s_maybe[k];

//MaxLikely = s_maybe;

}

}

//reverse(MaxLikely.begin(),MaxLikely.end());

for(i=s_length-1;i>=0;i--)

cout<<ch[MaxLikely[i]];

cout<<endl;

}

}

return 0;

}

代码的质量不敢说,只能说思路还是比较清晰的,而且看了曾宗根的书也用了一些数据结构,算是练个手吧,当然结果还是比较悲剧的。

后来在一位大神(OJ名”给数据跪了”,在此对您表示由衷的感谢)的帮助下,看懂了他的代码的思路,并开始用他的思路去思考问题。

他的思路是可以说是分级讨论问题,因为从后往前来看,需要找出的字符串的最后一个(第w个)字符是什么只取决于它前面一个(第w-1个)字符是什么,而第w-1个字符是什么取决于第w-2个字符……如此就形成了一个级联的问题。

首先已知REP[a][b] (Receiving Error Probabilities)即接收错误概率矩阵——发送a接收b的概率

和CSP[a][b] (CharacterSuccession Probabilities)即字符后继概率矩阵——发送a在发送b的概率

程序大致思路如下,假设只接收到一个字符s[0],很容易根据REP[a][s[0]]、REP[b][s[0]]来判断发送的是a还是b,如果此时再接收到一个字符s[1],就要进行讨论了

假设接收到的s[1]对应于发送a,对于前一个字符对应于a还是b,进行如下讨论:

如果是a,其概率为REP[a][s[0]]×CSP[a][a]:发送a接收s[0]的概率乘以发送a再发送a的概率

如果是b,其概率为REP[b][s[0]]×CSP[b][a]:发送b接收s[0]的概率乘以发送b再发送a的概率

这时就可以根据以上乘积的大小来判断出当s[1]对应于发送a时s[0]对应于发送什么了,然后可以得到s[1]对应于发送a的概率为

max{REP[a][s[0]]×CSP[a][a],REP[a][s[0]]×CSP[a][a]}×REP[a/b][s[1]]

(其中a/b对于与乘积的较大者)
同理,可得到s[1]对应于发送b的概率为

max{REP[a][s[0]]×CSP[a][b],REP[a][s[0]]×CSP[a][b]}×REP[a/b][s[1]]

(其中a/b对于与乘积的较大者)
并且当s[1]对应于发送a时s[0]对应于发送以上max函数中的较大者

如果继续接收到第三个字符,同样可以进行以上假设:

假设接收到的s[2]对应于发送a,对于前一个字符对应于a还是b,进行如下讨论:

如果是a,其概率为P(第二个字符是a的概率)×CSP[a][a]:第二个字符是a的概率乘以发送a再发送a的概率

如果是b,其概率为P(第二个字符是b的概率)×CSP[b][a]:第二个字符是b的概率乘以发送b再发送a的概率

而其中的P(第二个字符是a的概率)已经在前文中求的,所以可以用循环实现。

本算法的复杂度为a×a×w,远比aw小很多。(其中a是字符种类,w是字符串的长度)

我最终的代码如下:

#include <cstdlib>

#include <iostream>

#include <fstream>

#include <map>

#include <vector>

#include <string>

using namespace std;

int main(int argc, char *argv[])

{

fstreamcin("aaa.txt");

intn,a,i,j,num_case;

map<char,int>MAP_CH;

charc;

doubled;

chars[300];//string s;

cin>>n;

while(n--)

{

cin>> a; //读入字符种类数

charch[30];//vector<char> ch(a);

for(i=0;i<a;i++)

{

cin>>c;

ch[i]= c;

MAP_CH[c]= i;

//cout<<ch[i]<<endl;

}

//读入接受错误概率和字符继承概率矩阵

//注意下面这一行:vector<int后两个">"之间要有空格!否则会被认为是重载">>"。

doubleREP[30][30];//vector<vector<double> > REP(a, vector<double>(a)); //Receiving Error Probabilities

for(i=0;i<a;i++)

for(j=0;j<a;j++)

{

cin>>d;

REP[i][j]= d;

//cout<<REP[i][j]<<"";

}

doubleCSP[30][30];//vector<vector<double> > CSP(a, vector<double>(a)); //Character Succession Probabilities

for(i=0;i<a;i++)

for(j=0;j<a;j++)

{

cin>>d;

CSP[i][j]= d*10.0;

//cout<<CSP[i][j]<<"";

}

//读入测试案例

cin>>num_case;

while(num_case--)

{

cin>>s;

ints_length = strlen(s);//s.length();

vector<vector<double> > gl(s_length, vector<double>(a));

vector<vector<char> > answer(s_length, vector<char>(a));

for(i=0;i<a;i++)

gl[0][i] =REP[MAP_CH[ch[i]]][MAP_CH[s[0]]];

//cout<<gl[0][0]<<gl[0][1]<<endl;

for(int k=1; k<s_length;k++)

for(i=0;i<a;i++)

{

char c_last = ch[i]; //假设第k个字符是ch【i】

// 找出假设 c_last后的前一个字符(只可能有a种,对应概率为gl【k-1】【0~a-1】)的最大可能概率

double temp = 0;

for(j=0;j<a;j++)

{

if(temp< gl[k-1][MAP_CH[ch[j]]] * CSP[MAP_CH[ch[j]]][MAP_CH[c_last]])

{

temp = gl[k-1][MAP_CH[ch[j]]] * CSP[MAP_CH[ch[j]]][MAP_CH[c_last]];

answer[k][MAP_CH[c_last]] = ch[j]; //保存第k个字符是ch【i】时对应的第k-1个字符最大的可能的值

//cout<<answer[k][MAP_CH[c_last]]<<endl;

}

}

gl[k][MAP_CH[c_last]] =temp * REP[MAP_CH[c_last]][MAP_CH[s[k]]];

}

double temp = 0;

char ans_ch;

for(i=0;i<a;i++)

{

if(temp < gl[s_length-1][MAP_CH[ch[i]]])

{

temp =gl[s_length-1][MAP_CH[ch[i]]];

ans_ch =ch[i];//最后的一个最大概率字符

}

}

vector<char>s_answer(s_length);

s_answer[s_length-1]=ans_ch;

char ch_out = ans_ch;

for(i=s_length-2;i>=0;i--)

{ch_out = answer[i+1][MAP_CH[ch_out]];

s_answer[i]=ch_out; }

for(i=0;i<s_length;i++)

cout<<s_answer[i];

cout<<endl;

}

}

return0;

}

思路看懂后,很快就可以写程序了,但是有个比较纠结的问题,我发现读入CSP[][]矩阵(也就是发送字符后继概率矩阵)的时候,如果不保存为它的十倍(即*=10.0),提交的结果就是WrongAnswer,否则可以AC了。

这个问题困扰了我很久,自己的程序都快被改的乱七八糟了,百思不得其解,有点怀疑网站测试时的问题了,希望好心人给点提示。

再次感谢”给数据跪了”对我的启发。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: