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

作者分享了从初级到高级编程过程中遇到的挑战,包括使用HDOJ平台练习编程技巧,通过解决难题来提高自己在编程马拉松比赛中的表现。文章详细介绍了如何优化算法和数据结构,以及解决特定问题时采用的分级讨论法。此外,作者还探讨了如何避免代码超时错误,以及在读取特定矩阵数据时遇到的困惑,并最终通过调整数据处理方式解决了问题。

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

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

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

 

 

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

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值