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

来源:互联网 发布:如何数据恢复 编辑:程序博客网 时间:2024/05/21 10:07

最近想提高一下编程能力,参加了腾讯编程马拉松,结果五道题却制作出来了一道,有点受打击,于是就在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了。

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

 

 

再次感谢”给数据跪了”对我的启发。