UVa 1368 DNA Consensus String(贪心+计数)

原题地址

https://vjudge.net/problem/UVA-1368

题意:定义两个等长字符串的Hamming距离等于字符不同的位置个数。例如ACGT和GCGA的Hamming距离为2。(A-G和T-A不同)。
输入m个长度均为n的DNA序列(只含A、C、T、G),求解一个DNA序列,使得该序列到这m个序列的总Hamming距离之和最小。
如果存在多个这样的DNA序列,求字典序最小的解。

解题思路

本题是《算法竞赛入门经典》的习题3-7,属于字符串和贪心的结合题。

既然要求最小的总Hamming距离,那就尽可能使每一列的Hamming距离最小(最优子结构)。

  • 让答案序列的每一列都取这m个序列中对应列出现次数最多的字符,剩余其他字符的出现次数之和即该列的损耗,最后输出答案序列并累加这些损耗。
  • 结构体每个NODE单元的cnt表示该列elem字符出现的总次数,重载<符号使得之后的min函数取到最大的cnt,在cnt相同时取ASCII码最小的字符,保存在chose结构中。
  • ans[j].cnt = m - chose.cnt;每列字符出现的总次数一定是m,因此除去选定字符的频次,就是该列的距离之和。

ps:刚开始用优先队列priority_queue做,也能AC,后来觉得有点小题大做,毕竟只需要选出cnt最大元素即可,用一个三层min就可以了。

AC代码

#include <iostream>
#include <algorithm>
#define MAXM 55
#define MAXN 1005
using namespace std;

char seq[MAXM][MAXN];

typedef struct node
{
    char elem;
    int cnt;
    bool operator < (const node &A) const
    {
        if (this->cnt != A.cnt) //cnt降序
            return this->cnt > A.cnt; 
        else return this->elem < A.elem; //相同cnt字符升序
    }
}NODE;

int main()
{
    NODE ans[MAXN], a, c, g, t; //ans存放每一列的输出字符和该列的损耗
    int T, m, n;
    cin >> T;
    while (T--)
    {
        cin >> m >> n;
        for (int i = 0; i < m; ++i)
            cin >> seq[i];
        for (int j = 0; j < n; ++j) 
            ans[j].cnt = 0;
        for (int j = 0; j < n; ++j) //对每一列检查
        {
            //初始化
            a.elem = 'A'; a.cnt = 0;
            c.elem = 'C'; c.cnt = 0;
            g.elem = 'G'; g.cnt = 0;
            t.elem = 'T'; t.cnt = 0;
            for (int i = 0; i < m; ++i) //对该列的每一行检查
            {
                switch(seq[i][j]) //处理当前字符
                {
                case 'A': a.cnt++; break;
                case 'C': c.cnt++; break;
                case 'G': g.cnt++; break;
                case 'T': t.cnt++; break;
                default: break;
                }
            }
            NODE chose = min(a, min(c, min(t, g))); //重载后的min
            ans[j].elem = chose.elem; //取cnt最小的元素
            ans[j].cnt = m - chose.cnt; //其他元素的cnt和即这一列的损耗值
        }
        int errors = 0;
        for (int j = 0; j < n; ++j)
        {
            cout << ans[j].elem; 
            errors += ans[j].cnt; //损耗值累加
        }
        cout << endl;
        cout << errors << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值