【算法学习笔记】70.回文序列 动态规划 SJTU OJ 1066 小M家的牛们

这个题很多地方暗示了DP的路径。

我们处理时,dp[i][j]可以认为是从i坐标到j坐标的序列达到回文效果需要的最小代价,以此向外扩展,最终得到dp[0][M-1]就是结果。

我们要注意到处理dp[i][j]时,我们需要知道 dp(i+1,j-1)的结果,所以i必须降序,j必须升序,才能保证在计算dp(i,j)时,可以利用已经计算过的结果。

所以

i应该从M-2 到 0 递减

j在内层 从i+1到M-1 递增

在处理dp(i,j)时,第一要看name[i]和name[j]是否相等,如果相等的话,那么直接向内扣一层就可以了

dp[i][j] = dp[i+1][j-1]

否则我们要进行改造,使其成为回文字符串

这个改造有四种方法

1.先让i+1到j是回文的 再在最后加一个name[i]

2.先让i+1到j是回文的 再把前面的name[i]删掉

3.先让i到j-1是回文的 再在最前面加一个name[j]

4.先让i到j-1是回文的 再在最后的name[j]删掉

选择一种消耗最小的方案就可以了。

代码如下

#include <iostream>
#include <cstring>

using namespace std;

struct chr
{
    char c;
    int add;
    int del;
};

chr v[30];
int N,M;
string name;

void init(){
    cin>>N>>M;
    //N是不同字母的数量
    //M是原始名字的长度
    cin>>name;
    //记录字母cost情况
    for (int i = 0; i < N; ++i)
    {
        char c;
        cin>>c;
        v[c-‘a‘].c = c;
        cin >>v[c-‘a‘].add >> v[c-‘a‘].del;
    }
}

unsigned long long dp[2500][2500]={0};//存储的是 从第i个字母 到 第j个字母 的回文最小消耗

int build(){
    //从后向前填满dp[i][j]
    for (int i = M-2; i >=0; --i){
        for (int j = i + 1 ; j < M; ++j){
            if(name[i]==name[j])
                dp[i][j] = dp[i+1][j-1];//如果i和j相同 就向内扣一个 不用任何操作
            else{
                //如果不相同 要考虑四种修改方法
                dp[i][j] = 1<<30;
                unsigned long long cs[4];
                //注意 此时要保证 i+1,j 和 i,j-1都完成了 所以选择i降序 j升序
                cs[0] = dp[i+1][j] + v[name[i]-‘a‘].add; // 先让i+1到j是回文的 再在最后加一个i
                cs[1] = dp[i+1][j] + v[name[i]-‘a‘].del; // 先让i+1到j是回文的 再把前面的i删掉
                cs[2] = dp[i][j-1] + v[name[j]-‘a‘].add; // 先让i到j-1是回文的 再在最前面加一个j
                cs[3] = dp[i][j-1] + v[name[j]-‘a‘].del; // 先让i到j-1是回文的 再在最后的j删掉

                for(int k = 0; k<4; ++k)
                    dp[i][j] = min(dp[i][j],cs[k]);
            }
        }
    }
    return dp[0][M-1];
}
int main(int argc, char const *argv[])
{
    init();
    cout<<build()<<endl;
    return 0;
}
时间: 2024-10-11 02:40:29

【算法学习笔记】70.回文序列 动态规划 SJTU OJ 1066 小M家的牛们的相关文章

【算法学习笔记】31.动态规划 SJTU OJ 1320 numtri

Description Consider the number triangle shown below. Write a program that calculates the highest sum of numbers that can be passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left

【算法学习笔记】63. BFS SJTU OJ 1281 蹦蹦跳跳

典型的BFS,找到起点直接进行搜搜即可.要注意的就是层数的处理.坐标到id的转换.还有就是要尽早判断是否达到终点. 代码注释很详细,最后面两个函数是一开始写的 用抽取邻接矩阵+Dijkstra 来算的,很麻烦 头脑一热的结果.. #include <vector> #include <queue> #include <iostream> using namespace std; int map[31][31]={0}; int M,N,M1,M2; struct Poi

【算法学习笔记】68.枚举 SJTU OJ 1272 写数游戏

很简单 不用太考虑效率 虽然每次都要重新排序 注意vector的使用,非常便利. 还有一个技巧就是用一个have型bool数组来记录是否存在. #include <iostream> #include <vector> #include <algorithm> using namespace std; bool have[1000] = {0}; vector<int> v; //用vector来实现动态数组的简便性 bool cmp_int(const i

【算法学习笔记】71.动态规划 双重条件 SJTU OJ 1124 我把助教团的平均智商拉低了

这个题第一眼以为只是一个简单的序列型DP问题,于是快速的写下了下面的代码. #include <iostream> using namespace std; /* 看着只是简单的序列DP..不知道有没有坑 DP[k]表示的是有前k个活动可以选择时的答案 */ struct state { int health;//体力 永远大于0 int wisdom;//智力 最后大于70 int product;//乘积 void calc(){ product = health * wisdom; }

【算法学习笔记】43.动态规划 逆向思维 SJTU OJ 1012 增长率问题

1012. 增长率问题 Description 有一个数列,它是由自然数组成的,并且严格单调上升.最小的数不小于S,最大的不超过T.现在知道这个数列有一个性质:后一个数相对于前一个数的增长率总是百分比下的整数(如5相对于4的增长率是25%,25为整数:而9对7就不行了).现在问:这个数列最长可以有多长?满足最长要求的数列有多少个? Input Format 输入仅有一行,包含S和T两个数( 0<S<T≤200000 ). 30%的数据,0<S<T≤100 : 100%的数据,0&l

【算法学习笔记】72.LCS 最大公公子序列 动态规划 SJTU OJ 1065 小M的生物实验1

非常简单的DP 如果dp[i,j]表示从0到i 和 从0到j 这两段的相似度, 那么可以知道每个dp[i,j]是由三种状态转化过来的 第一种 当dna1[i]==dna2[j]的时候 dp[i-1,j-1] +  1  长度加1 第二种  否则 从下面两个状态过来那就是 dp[i][j-1] 和 dp[i-1][j]//注意因为是顺序遍历 这两个都已经计算过 取两者最大即可. #include <iostream> #include <cstring> #include <a

【算法学习笔记】27.动态规划 解题报告 SJTU_OJ 1254 传手绢

1254. 传手绢 Description 活动的时候,老师经常带着同学们一起做游戏.这次,老师带着同学们一起传手绢. 游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着手绢,当老师吹哨子时开始传,每个同学可以把手绢传给自己左右的两个同学中的一个(左右任意),当老师在此吹哨子时,游戏停止,此时,拿着手绢的那个同学要给大家表演一个节目. abc提出一个有趣的问题:有多少种不同的传手绢方法可以使得从abc手里开始传的手绢,传了m次以后,又回到abc手里.两种传手绢方法被视作不同的方法,当

【算法学习笔记】75. 动态规划 棋盘型 期望计算 1390 畅畅的牙签盒(改)

一开始用了模拟的方法,DFS来搜索,但是因为当n很大的时候有很多的重复计算,因为会踏过重复的点进行重复的操作,而且不能不走这些重复的路径,因为没有存储结果,最后只过了三个点. 考虑到重复的路径,所以想到利用动态规划. 可以认为dp[i][j]表示的是 从左上角开始走,走出以(1,1)到(i,j)为两个端点的棋盘中拿到牙签袋的期望. 画图可以知道,因为每次都是斜向下走.走出 i,j棋盘,可以是先走出比这个棋盘小的36种小棋盘的任何一种,然后再吃到在小棋盘和i,j中的某一个,拿到的牙签期望. (如果

【算法学习笔记】30.动态规划 01背包和完全背包的关系

首先先说明一下01背包和完全背包问题的区别 01背包:有 N 件物品和一个容量为 V 的背包.放入第 i 件物品耗费的费用是 Ci,得到的价值是 Wi.求解将哪些物品装入背包可使价值总和最大.(可以不装满) 完全背包:有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用.放入第 i 种物品 的费用是 Ci,价值是 Wi.求解:将哪些物品装入背包,可使这些物品的耗费的费用总和不超过背包容量,且价值总和最大. 先说结论: 两个问题的最优解都要用DP来解决,实现的过程也非常像只是在内层循环中