zoukankan      html  css  js  c++  java
  • GSM短信息部分代码!(转个人收藏)

      1 #include "stdafx.h"
      2 #include "Sms.h"
      3 #include "Comm.h"
      4 
      5 // 可打印字符串转换为字节数据
      6 // 如:"C8329BFD0E01" --> {0xC8, 0x32, 0x9B, 0xFD, 0x0E, 0x01}
      7 // 输入: pSrc - 源字符串指针
      8 //       nSrcLength - 源字符串长度
      9 // 输出: pDst - 目标数据指针
     10 // 返回: 目标数据长度
     11 int gsmString2Bytes(const char* pSrc, unsigned char* pDst, int nSrcLength)
     12 {
     13     for (int i = 0; i < nSrcLength; i += 2)
     14     {
     15         // 输出高4位
     16         if ((*pSrc >= '0') && (*pSrc <= '9'))
     17         {
     18             *pDst = (*pSrc - '0') << 4;
     19         }
     20         else
     21         {
     22             *pDst = (*pSrc - 'A' + 10) << 4;
     23         }
     24 
     25         pSrc++;
     26 
     27         // 输出低4位
     28         if ((*pSrc>='0') && (*pSrc<='9'))
     29         {
     30             *pDst |= *pSrc - '0';
     31         }
     32         else
     33         {
     34             *pDst |= *pSrc - 'A' + 10;
     35         }
     36 
     37         pSrc++;
     38         pDst++;
     39     }
     40 
     41     // 返回目标数据长度
     42     return (nSrcLength / 2);
     43 }
     44 
     45 // 字节数据转换为可打印字符串
     46 // 如:{0xC8, 0x32, 0x9B, 0xFD, 0x0E, 0x01} --> "C8329BFD0E01" 
     47 // 输入: pSrc - 源数据指针
     48 //       nSrcLength - 源数据长度
     49 // 输出: pDst - 目标字符串指针
     50 // 返回: 目标字符串长度
     51 int gsmBytes2String(const unsigned char* pSrc, char* pDst, int nSrcLength)
     52 {
     53     const char tab[]="0123456789ABCDEF";    // 0x0-0xf的字符查找表
     54 
     55     for (int i = 0; i < nSrcLength; i++)
     56     {
     57         *pDst++ = tab[*pSrc >> 4];        // 输出高4位
     58         *pDst++ = tab[*pSrc & 0x0f];    // 输出低4位
     59         pSrc++;
     60     }
     61 
     62     // 输出字符串加个结束符
     63     *pDst = '\0';
     64 
     65     // 返回目标字符串长度
     66     return (nSrcLength * 2);
     67 }
     68 
     69 // 7bit编码
     70 // 输入: pSrc - 源字符串指针
     71 //       nSrcLength - 源字符串长度
     72 // 输出: pDst - 目标编码串指针
     73 // 返回: 目标编码串长度
     74 int gsmEncode7bit(const char* pSrc, unsigned char* pDst, int nSrcLength)
     75 {
     76     int nSrc;        // 源字符串的计数值
     77     int nDst;        // 目标编码串的计数值
     78     int nChar;        // 当前正在处理的组内字符字节的序号,范围是0-7
     79     unsigned char nLeft;    // 上一字节残余的数据
     80 
     81     // 计数值初始化
     82     nSrc = 0;
     83     nDst = 0;
     84 
     85     // 将源串每8个字节分为一组,压缩成7个字节
     86     // 循环该处理过程,直至源串被处理完
     87     // 如果分组不到8字节,也能正确处理
     88     while (nSrc < nSrcLength)
     89     {
     90         // 取源字符串的计数值的最低3位
     91         nChar = nSrc & 7;
     92 
     93         // 处理源串的每个字节
     94         if(nChar == 0)
     95         {
     96             // 组内第一个字节,只是保存起来,待处理下一个字节时使用
     97             nLeft = *pSrc;
     98         }
     99         else
    100         {
    101             // 组内其它字节,将其右边部分与残余数据相加,得到一个目标编码字节
    102             *pDst = (*pSrc << (8-nChar)) | nLeft;
    103 
    104             // 将该字节剩下的左边部分,作为残余数据保存起来
    105             nLeft = *pSrc >> nChar;
    106 
    107             // 修改目标串的指针和计数值
    108             pDst++;
    109             nDst++;
    110         }
    111 
    112         // 修改源串的指针和计数值
    113         pSrc++;
    114         nSrc++;
    115     }
    116 
    117     // 返回目标串长度
    118     return nDst;
    119 }
    120 
    121 // 7bit解码
    122 // 输入: pSrc - 源编码串指针
    123 //       nSrcLength - 源编码串长度
    124 // 输出: pDst - 目标字符串指针
    125 // 返回: 目标字符串长度
    126 int gsmDecode7bit(const unsigned char* pSrc, char* pDst, int nSrcLength)
    127 {
    128     int nSrc;        // 源字符串的计数值
    129     int nDst;        // 目标解码串的计数值
    130     int nByte;        // 当前正在处理的组内字节的序号,范围是0-6
    131     unsigned char nLeft;    // 上一字节残余的数据
    132 
    133     // 计数值初始化
    134     nSrc = 0;
    135     nDst = 0;
    136     
    137     // 组内字节序号和残余数据初始化
    138     nByte = 0;
    139     nLeft = 0;
    140 
    141     // 将源数据每7个字节分为一组,解压缩成8个字节
    142     // 循环该处理过程,直至源数据被处理完
    143     // 如果分组不到7字节,也能正确处理
    144     while(nSrc<nSrcLength)
    145     {
    146         // 将源字节右边部分与残余数据相加,去掉最高位,得到一个目标解码字节
    147         *pDst = ((*pSrc << nByte) | nLeft) & 0x7f;
    148 
    149         // 将该字节剩下的左边部分,作为残余数据保存起来
    150         nLeft = *pSrc >> (7-nByte);
    151 
    152         // 修改目标串的指针和计数值
    153         pDst++;
    154         nDst++;
    155 
    156         // 修改字节计数值
    157         nByte++;
    158 
    159         // 到了一组的最后一个字节
    160         if(nByte == 7)
    161         {
    162             // 额外得到一个目标解码字节
    163             *pDst = nLeft;
    164 
    165             // 修改目标串的指针和计数值
    166             pDst++;
    167             nDst++;
    168 
    169             // 组内字节序号和残余数据初始化
    170             nByte = 0;
    171             nLeft = 0;
    172         }
    173 
    174         // 修改源串的指针和计数值
    175         pSrc++;
    176         nSrc++;
    177     }
    178 
    179     // 输出字符串加个结束符
    180     *pDst = '\0';
    181 
    182     // 返回目标串长度
    183     return nDst;
    184 }
    185 
    186 // 8bit编码
    187 // 输入: pSrc - 源字符串指针
    188 //       nSrcLength - 源字符串长度
    189 // 输出: pDst - 目标编码串指针
    190 // 返回: 目标编码串长度
    191 int gsmEncode8bit(const char* pSrc, unsigned char* pDst, int nSrcLength)
    192 {
    193     // 简单复制
    194     memcpy(pDst, pSrc, nSrcLength);
    195 
    196     return nSrcLength;
    197 }
    198 
    199 // 8bit解码
    200 // 输入: pSrc - 源编码串指针
    201 //       nSrcLength -  源编码串长度
    202 // 输出: pDst -  目标字符串指针
    203 // 返回: 目标字符串长度
    204 int gsmDecode8bit(const unsigned char* pSrc, char* pDst, int nSrcLength)
    205 {
    206     // 简单复制
    207     memcpy(pDst, pSrc, nSrcLength);
    208 
    209     // 输出字符串加个结束符
    210     *pDst = '\0';
    211 
    212     return nSrcLength;
    213 }
    214 
    215 // UCS2编码
    216 // 输入: pSrc - 源字符串指针
    217 //       nSrcLength - 源字符串长度
    218 // 输出: pDst - 目标编码串指针
    219 // 返回: 目标编码串长度
    220 int gsmEncodeUcs2(const char* pSrc, unsigned char* pDst, int nSrcLength)
    221 {
    222     int nDstLength;        // UNICODE宽字符数目
    223     WCHAR wchar[128];    // UNICODE串缓冲区
    224 
    225     // 字符串-->UNICODE串
    226     nDstLength = MultiByteToWideChar(CP_ACP, 0, pSrc, nSrcLength, wchar, 128);
    227 
    228     // 高低字节对调,输出
    229     for(int i=0; i<nDstLength; i++)
    230     {
    231         *pDst++ = wchar[i] >> 8;        // 先输出高位字节
    232         *pDst++ = wchar[i] & 0xff;        // 后输出低位字节
    233     }
    234 
    235     // 返回目标编码串长度
    236     return nDstLength * 2;
    237 }
    238 
    239 // UCS2解码
    240 // 输入: pSrc - 源编码串指针
    241 //       nSrcLength -  源编码串长度
    242 // 输出: pDst -  目标字符串指针
    243 // 返回: 目标字符串长度
    244 int gsmDecodeUcs2(const unsigned char* pSrc, char* pDst, int nSrcLength)
    245 {
    246     int nDstLength;        // UNICODE宽字符数目
    247     WCHAR wchar[128];    // UNICODE串缓冲区
    248 
    249     // 高低字节对调,拼成UNICODE
    250     for(int i=0; i<nSrcLength/2; i++)
    251     {
    252         wchar[i] = *pSrc++ << 8;    // 先高位字节
    253         wchar[i] |= *pSrc++;        // 后低位字节
    254     }
    255 
    256     // UNICODE串-->字符串
    257     nDstLength = WideCharToMultiByte(CP_ACP, 0, wchar, nSrcLength/2, pDst, 160, NULL, NULL);
    258 
    259     // 输出字符串加个结束符
    260     pDst[nDstLength] = '\0';
    261 
    262     // 返回目标字符串长度
    263     return nDstLength;
    264 }
    265 
    266 // 正常顺序的字符串转换为两两颠倒的字符串,若长度为奇数,补'F'凑成偶数
    267 // 如:"8613851872468" --> "683158812764F8"
    268 // 输入: pSrc - 源字符串指针
    269 //       nSrcLength - 源字符串长度
    270 // 输出: pDst - 目标字符串指针
    271 // 返回: 目标字符串长度
    272 int gsmInvertNumbers(const char* pSrc, char* pDst, int nSrcLength)
    273 {
    274     int nDstLength;        // 目标字符串长度
    275     char ch;            // 用于保存一个字符
    276 
    277     // 复制串长度
    278     nDstLength = nSrcLength;
    279 
    280     // 两两颠倒
    281     for(int i=0; i<nSrcLength;i+=2)
    282     {
    283         ch = *pSrc++;        // 保存先出现的字符
    284         *pDst++ = *pSrc++;    // 复制后出现的字符
    285         *pDst++ = ch;        // 复制先出现的字符
    286     }
    287 
    288     // 源串长度是奇数吗?
    289     if(nSrcLength & 1)
    290     {
    291         *(pDst-2) = 'F';    // 补'F'
    292         nDstLength++;        // 目标串长度加1
    293     }
    294 
    295     // 输出字符串加个结束符
    296     *pDst = '\0';
    297 
    298     // 返回目标字符串长度
    299     return nDstLength;
    300 }
    301 
    302 // 两两颠倒的字符串转换为正常顺序的字符串
    303 // 如:"683158812764F8" --> "8613851872468"
    304 // 输入: pSrc - 源字符串指针
    305 //       nSrcLength - 源字符串长度
    306 // 输出: pDst - 目标字符串指针
    307 // 返回: 目标字符串长度
    308 int gsmSerializeNumbers(const char* pSrc, char* pDst, int nSrcLength)
    309 {
    310     int nDstLength;        // 目标字符串长度
    311     char ch;            // 用于保存一个字符
    312 
    313     // 复制串长度
    314     nDstLength = nSrcLength;
    315 
    316     // 两两颠倒
    317     for(int i=0; i<nSrcLength;i+=2)
    318     {
    319         ch = *pSrc++;        // 保存先出现的字符
    320         *pDst++ = *pSrc++;    // 复制后出现的字符
    321         *pDst++ = ch;        // 复制先出现的字符
    322     }
    323 
    324     // 最后的字符是'F'吗?
    325     if(*(pDst-1) == 'F')
    326     {
    327         pDst--;
    328         nDstLength--;        // 目标字符串长度减1
    329     }
    330 
    331     // 输出字符串加个结束符
    332     *pDst = '\0';
    333 
    334     // 返回目标字符串长度
    335     return nDstLength;
    336 }
    337 
    338 // PDU编码,用于编制、发送短消息
    339 // 输入: pSrc - 源PDU参数指针
    340 // 输出: pDst - 目标PDU串指针
    341 // 返回: 目标PDU串长度
    342 int gsmEncodePdu(const SM_PARAM* pSrc, char* pDst)
    343 {
    344     int nLength;            // 内部用的串长度
    345     int nDstLength;            // 目标PDU串长度
    346     unsigned char buf[256];    // 内部用的缓冲区
    347 
    348     // SMSC地址信息段
    349     nLength = strlen(pSrc->SCA);    // SMSC地址字符串的长度    
    350     buf[0] = (char)((nLength & 1) == 0 ? nLength : nLength + 1) / 2 + 1;    // SMSC地址信息长度
    351     buf[1] = 0x91;        // 固定: 用国际格式号码
    352     nDstLength = gsmBytes2String(buf, pDst, 2);        // 转换2个字节到目标PDU串
    353     nDstLength += gsmInvertNumbers(pSrc->SCA, &pDst[nDstLength], nLength);    // 转换SMSC号码到目标PDU串
    354 
    355     // TPDU段基本参数、目标地址等
    356     nLength = strlen(pSrc->TPA);    // TP-DA地址字符串的长度
    357     buf[0] = 0x11;                    // 是发送短信(TP-MTI=01),TP-VP用相对格式(TP-VPF=10)
    358     buf[1] = 0;                        // TP-MR=0
    359     buf[2] = (char)nLength;            // 目标地址数字个数(TP-DA地址字符串真实长度)
    360     buf[3] = 0x91;                    // 固定: 用国际格式号码
    361     nDstLength += gsmBytes2String(buf, &pDst[nDstLength], 4);        // 转换4个字节到目标PDU串
    362     nDstLength += gsmInvertNumbers(pSrc->TPA, &pDst[nDstLength], nLength);    // 转换TP-DA到目标PDU串
    363 
    364     // TPDU段协议标识、编码方式、用户信息等
    365     nLength = strlen(pSrc->TP_UD);    // 用户信息字符串的长度
    366     buf[0] = pSrc->TP_PID;            // 协议标识(TP-PID)
    367     buf[1] = pSrc->TP_DCS;            // 用户信息编码方式(TP-DCS)
    368     buf[2] = 0;                        // 有效期(TP-VP)为5分钟
    369     if(pSrc->TP_DCS == GSM_7BIT)    
    370     {
    371         // 7-bit编码方式
    372         buf[3] = nLength;            // 编码前长度
    373         nLength = gsmEncode7bit(pSrc->TP_UD, &buf[4], nLength+1) + 4;    // 转换TP-DA到目标PDU串
    374     }
    375     else if(pSrc->TP_DCS == GSM_UCS2)
    376     {
    377         // UCS2编码方式
    378         buf[3] = gsmEncodeUcs2(pSrc->TP_UD, &buf[4], nLength);    // 转换TP-DA到目标PDU串
    379         nLength = buf[3] + 4;        // nLength等于该段数据长度
    380     }
    381     else
    382     {
    383         // 8-bit编码方式
    384         buf[3] = gsmEncode8bit(pSrc->TP_UD, &buf[4], nLength);    // 转换TP-DA到目标PDU串
    385         nLength = buf[3] + 4;        // nLength等于该段数据长度
    386     }
    387     nDstLength += gsmBytes2String(buf, &pDst[nDstLength], nLength);        // 转换该段数据到目标PDU串
    388 
    389     // 返回目标字符串长度
    390     return nDstLength;
    391 }
    392 
    393 // PDU解码,用于接收、阅读短消息
    394 // 输入: pSrc - 源PDU串指针
    395 // 输出: pDst - 目标PDU参数指针
    396 // 返回: 用户信息串长度
    397 int gsmDecodePdu(const char* pSrc, SM_PARAM* pDst)
    398 {
    399     int nDstLength;            // 目标PDU串长度
    400     unsigned char tmp;        // 内部用的临时字节变量
    401     unsigned char buf[256];    // 内部用的缓冲区
    402 
    403     // SMSC地址信息段
    404     gsmString2Bytes(pSrc, &tmp, 2);    // 取长度
    405     tmp = (tmp - 1) * 2;    // SMSC号码串长度
    406     pSrc += 4;            // 指针后移,忽略了SMSC地址格式
    407     gsmSerializeNumbers(pSrc, pDst->SCA, tmp);    // 转换SMSC号码到目标PDU串
    408     pSrc += tmp;        // 指针后移
    409 
    410     // TPDU段基本参数
    411     gsmString2Bytes(pSrc, &tmp, 2);    // 取基本参数
    412     pSrc += 2;        // 指针后移
    413 
    414     // 取回复号码
    415     gsmString2Bytes(pSrc, &tmp, 2);    // 取长度
    416     if(tmp & 1) tmp += 1;    // 调整奇偶性
    417     pSrc += 4;            // 指针后移,忽略了回复地址(TP-RA)格式
    418     gsmSerializeNumbers(pSrc, pDst->TPA, tmp);    // 取TP-RA号码
    419     pSrc += tmp;        // 指针后移
    420 
    421     // TPDU段协议标识、编码方式、用户信息等
    422     gsmString2Bytes(pSrc, (unsigned char*)&pDst->TP_PID, 2);    // 取协议标识(TP-PID)
    423     pSrc += 2;        // 指针后移
    424     gsmString2Bytes(pSrc, (unsigned char*)&pDst->TP_DCS, 2);    // 取编码方式(TP-DCS)
    425     pSrc += 2;        // 指针后移
    426     gsmSerializeNumbers(pSrc, pDst->TP_SCTS, 14);        // 服务时间戳字符串(TP_SCTS) 
    427     pSrc += 14;        // 指针后移
    428     gsmString2Bytes(pSrc, &tmp, 2);    // 用户信息长度(TP-UDL)
    429     pSrc += 2;        // 指针后移
    430     if(pDst->TP_DCS == GSM_7BIT)    
    431     {
    432         // 7-bit解码
    433         nDstLength = gsmString2Bytes(pSrc, buf, tmp & 7 ? (int)tmp * 7 / 4 + 2 : (int)tmp * 7 / 4);    // 格式转换
    434         gsmDecode7bit(buf, pDst->TP_UD, nDstLength);    // 转换到TP-DU
    435         nDstLength = tmp;
    436     }
    437     else if(pDst->TP_DCS == GSM_UCS2)
    438     {
    439         // UCS2解码
    440         nDstLength = gsmString2Bytes(pSrc, buf, tmp * 2);            // 格式转换
    441         nDstLength = gsmDecodeUcs2(buf, pDst->TP_UD, nDstLength);    // 转换到TP-DU
    442     }
    443     else
    444     {
    445         // 8-bit解码
    446         nDstLength = gsmString2Bytes(pSrc, buf, tmp * 2);            // 格式转换
    447         nDstLength = gsmDecode8bit(buf, pDst->TP_UD, nDstLength);    // 转换到TP-DU
    448     }
    449 
    450     // 返回目标字符串长度
    451     return nDstLength;
    452 }
    453 
    454 // 初始化GSM状态
    455 BOOL gsmInit()
    456 {
    457     char ans[128];        // 应答串
    458 
    459     // 测试GSM-MODEM的存在性
    460     WriteComm("AT\r", 3);
    461     ReadComm(ans, 128);
    462     if (strstr(ans, "OK") == NULL)  return FALSE;
    463 
    464     // ECHO OFF
    465     WriteComm("ATE0\r", 5);
    466     ReadComm(ans, 128);
    467 
    468     // PDU模式
    469     WriteComm("AT+CMGF=0\r", 10);
    470     ReadComm(ans, 128);
    471 
    472     return TRUE;
    473 }
    474 
    475 // 发送短消息,仅发送命令,不读取应答
    476 // 输入: pSrc - 源PDU参数指针
    477 int gsmSendMessage(SM_PARAM* pSrc)
    478 {
    479     int nPduLength;        // PDU串长度
    480     unsigned char nSmscLength;    // SMSC串长度
    481     int nLength;        // 串口收到的数据长度
    482     char cmd[16];        // 命令串
    483     char pdu[512];        // PDU串
    484     char ans[128];        // 应答串
    485 
    486     nPduLength = gsmEncodePdu(pSrc, pdu);    // 根据PDU参数,编码PDU串
    487     strcat(pdu, "\x01a");        // 以Ctrl-Z结束
    488 
    489     gsmString2Bytes(pdu, &nSmscLength, 2);    // 取PDU串中的SMSC信息长度
    490     nSmscLength++;        // 加上长度字节本身
    491 
    492     // 命令中的长度,不包括SMSC信息长度,以数据字节计
    493     sprintf(cmd, "AT+CMGS=%d\r", nPduLength / 2 - nSmscLength);    // 生成命令
    494 
    495 //    TRACE("%s", cmd);
    496 //    TRACE("%s\n", pdu);
    497 
    498     WriteComm(cmd, strlen(cmd));    // 先输出命令串
    499 
    500     nLength = ReadComm(ans, 128);    // 读应答数据
    501 
    502     // 根据能否找到"\r\n> "决定成功与否
    503     if(nLength == 4 && strncmp(ans, "\r\n> ", 4) == 0)
    504     {
    505         return WriteComm(pdu, strlen(pdu));        // 得到肯定回答,继续输出PDU串
    506     }
    507 
    508     return 0;
    509 }
    510 
    511 // 读取短消息,仅发送命令,不读取应答
    512 // 用+CMGL代替+CMGR,可一次性读出全部短消息
    513 int gsmReadMessageList()
    514 {
    515     return WriteComm("AT+CMGL\r", 8);
    516 }
    517 
    518 // 删除短消息,仅发送命令,不读取应答
    519 // 输入: index - 短消息序号,1-255
    520 int gsmDeleteMessage(int index)
    521 {
    522     char cmd[16];        // 命令串
    523 
    524     sprintf(cmd, "AT+CMGD=%d\r", index);    // 生成命令
    525 
    526     // 输出命令串
    527     return WriteComm(cmd, strlen(cmd));
    528 }
    529 
    530 // 读取GSM MODEM的应答,可能是一部分
    531 // 输出: pBuff - 接收应答缓冲区
    532 // 返回: GSM MODEM的应答状态, GSM_WAIT/GSM_OK/GSM_ERR
    533 // 备注: 可能需要多次调用才能完成读取一次应答,首次调用时应将pBuff初始化
    534 int gsmGetResponse(SM_BUFF* pBuff)
    535 {
    536     int nLength;        // 串口收到的数据长度
    537     int nState;
    538 
    539     // 从串口读数据,追加到缓冲区尾部
    540     nLength = ReadComm(&pBuff->data[pBuff->len], 128);    
    541     pBuff->len += nLength;
    542 
    543     // 确定GSM MODEM的应答状态
    544     nState = GSM_WAIT;
    545     if ((nLength > 0) && (pBuff->len >= 4))
    546     {
    547         if (strncmp(&pBuff->data[pBuff->len - 4], "OK\r\n", 4) == 0)  nState = GSM_OK;
    548         else if (strstr(pBuff->data, "+CMS ERROR") != NULL) nState = GSM_ERR;
    549     }
    550 
    551     return nState;
    552 }
    553 
    554 // 从列表中解析出全部短消息
    555 // 输入: pBuff - 短消息列表缓冲区
    556 // 输出: pMsg - 短消息缓冲区
    557 // 返回: 短消息条数
    558 int gsmParseMessageList(SM_PARAM* pMsg, SM_BUFF* pBuff)
    559 {
    560     int nMsg;            // 短消息计数值
    561     char* ptr;            // 内部用的数据指针
    562 
    563     nMsg = 0;
    564     ptr = pBuff->data;
    565 
    566     // 循环读取每一条短消息, 以"+CMGL:"开头
    567     while((ptr = strstr(ptr, "+CMGL:")) != NULL)
    568     {
    569         ptr += 6;        // 跳过"+CMGL:", 定位到序号
    570         sscanf(ptr, "%d", &pMsg->index);    // 读取序号
    571 //        TRACE("  index=%d\n",pMsg->index);
    572 
    573         ptr = strstr(ptr, "\r\n");    // 找下一行
    574         if (ptr != NULL)
    575         {
    576             ptr += 2;        // 跳过"\r\n", 定位到PDU
    577             
    578             gsmDecodePdu(ptr, pMsg);    // PDU串解码
    579 
    580             pMsg++;        // 准备读下一条短消息
    581             nMsg++;        // 短消息计数加1
    582         }
    583     }
    584 
    585     return nMsg;
    586 }
  • 相关阅读:
    POJ 1300 Open Door
    POJ 2230 Watchcow
    codevs 1028 花店橱窗布置
    codevs 1021 玛丽卡
    codevs 1519 过路费
    codevs 3287 货车运输
    codevs 3305 水果姐逛水果街二
    codevs 1036 商务旅行
    codevs 4605 LCA
    POJ 1330 Nearest Common Ancestors
  • 原文地址:https://www.cnblogs.com/kernel0815/p/2509548.html
Copyright © 2011-2022 走看看