太久没碰动态规划了,完全忘记了...
在网上学了一下,写出了超时的DP....
最后决定投奔y总orz
废话不多说,上分析
动态规划简介
1.什么是动态规划?
——通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法
说简单点,就是把复杂问题,拆分成子问题,再求解子问题时,记住答案,减少重复计算
2.什么问题可以用动态规划?
——求最值的问题,如最长上升子序列、最小编辑距离、背包问题、凑零钱问题等
如何分析动态规划问题
看了这么多讲解dp的博文,还得是y总的 思想,简单清晰易懂
按照y总的分析法:
DP :
(1)状态表示
<1>集合:f[i]:以i结尾的所有接龙子序列的集合
<2>属性:子序列长度的最大值
(2)状态计算
这个集合中的子序列都是以i结尾的,看倒数第二个数,可以为:无、a1、a2....ai - 1
求集合的max,只须求每个子集合的值,取max即可
当取无时,f[i] = 1(题意说了,当只有一个数时,长度为1)
分析一般情况:倒数第二位数为aj,即......aj ai 那么此时,只须分析.......aj的max,......aj的定义是所有以aj 结尾的接龙子序列的集合,即f[j]
得到 f[i] = max(f[j] + 1)
本题的分析
本题求的是最少删除多少数 == 找到一个最长子序列使得它是接龙序列(求最值——DP!)
与最长上升子序列类似:每个数能否放在一个数的前面只与它前一个数有关(>前一个数即可)
接龙序列:每个数能否选,只取决于该数的第一位数与前一个数的最后一位数是否相等
DP求解过程见上
代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
int f[N],l[N],r[N],n;
int main()
{
scanf("%d",&n);
char c[20]; //只须求首位和末位数字,用字符串存
for(int i = 0;i < n;i ++)
{
scanf("%s",c);
l[i] = c[0] - '0',r[i] = c[strlen(c) - 1] - '0';
}
int res = 0;
for(int i = 0;i < n;i ++)
{
f[i] = 1; //无
for(int j = 0;j < i;j ++) //子集合
{
if(r[j] == l[i])
{
f[i] = max(f[i],f[j] + 1);
}
}
res = max(res,f[i]); //记录最长的子序列
}
printf("%d",n - res);
return 0;
}
优化
这么交上去,当然会TLE
时间复杂度O(N^2)
仔细分析:在循环j时,其实只需要关心最后一位数=i首位的数
优化方法:开一个数组g[0~9]:g[x]以x为最后一位数的接龙子序列的最大值
优化结果:删除一层for,f[i]直接与g[l[i]]+1比较取max
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
int f[N],l[N],r[N],g[10],n;
int main()
{
scanf("%d",&n);
char c[20];
for(int i = 0;i < n;i ++)
{
scanf("%s",c);
l[i] = c[0] - '0',r[i] = c[strlen(c) - 1] - '0';
}
int res = 0;
for(int i = 0;i < n;i ++)
{
f[i] = 1;
f[i] = max(f[i],g[l[i]] + 1);
g[r[i]] = max(g[r[i]],f[i]); //计算出了f[i],记得更新g[]
res = max(res,f[i]);
}
printf("%d",n - res);
return 0;
}
至此,已经可以ac了,but!真给y总跪了orzzz,还能优化代码
1.因为每次都只用到f[i],i前面的数都没有用,so不用开f[],直接定义一个变量
2.每次也只用一次l[i],r[i],so直接定义一个l、r即可,边输入,边计算
优化代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
int g[10],n;
int main()
{
scanf("%d",&n);
char c[20];
int res = 0;
int f,l,r;
for(int i = 0;i < n;i ++)
{
scanf("%s",c);
l = c[0] - '0',r = c[strlen(c) - 1] - '0';
f = 1;
f = max(f,g[l] + 1);
g[r] = max(g[r],f);
res = max(res,f);
}
printf("%d",n - res);
return 0;
}
如此简短优雅的代码,真的让我以为蓝桥杯sooo easy.......