Daliy Algorithm (dp,堆)-- day 79
Nothing to fear
种一棵树最好的时间是十年前,其次是现在!
那些你早出晚归付出的刻苦努力,你不想训练,当你觉的太累了但还是要咬牙坚持的时候,那就是在追逐梦想,不要在意终点有什么,要享受路途的过程,或许你不能成就梦想,但一定会有更伟大的事情随之而来。 mamba out~
2020.5.15
人一我十,人十我百,追逐青春的梦想,怀着自信的心,永不言弃!
Constructing the Array
构造算法,和优先队列得使用,优先队列好久不用都有点忘记了,回头总结什么时候用优先队列比较合适。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <cassert>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <queue>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define lowbit(x) (x & -x)
using namespace std;
typedef long long ll;
const int MAX = 0x7ffffff;
int t;
struct node{
int l , r , len;
bool operator < (const node &x) const
{
if(len == x.len)return l > x.l;
else return len < x.len;
}
};
void slove()
{
int n;
cin >> n;
vector<int> a(n + 1);
priority_queue<node> q;
q.push({1 , n , n});
int val = 1;
while(!q.empty())
{
node now = q.top();
//printf("now segment : [%d,%d]\n",now.l,now.r);
q.pop();
int k = now.r - now.l + 1;
int mid = 0;
if(k % 2 != 0)
{
mid = now.l + now.r >> 1;
a[mid] = val;
}else {
mid = now.l + now.r - 1 >> 1;
a[mid] = val;
}
//printf("left :[%d,%d] right[%d,%d]\n",now.l,mid-1,mid+1,now.r);
int l = now.l, r = mid - 1;
if(l <= r)q.push({now.l , mid-1,mid - now.l});
l = mid + 1,r = now.r;
if(l <= r)q.push({mid + 1,now.r , now.r - mid});
val++;
}
for(int i = 1;i <= n ;i ++) cout << a[i] << " ";
cout << endl;
return ;
}
int main()
{
#ifdef LOCAL
auto start_time = clock();
cerr << setprecision(3) << fixed; // 在iomanip中
#endif
SIS;
cin >> t;
while(t--)
{
slove();
}
#ifdef LOCAL
auto end_time = clock();
cerr << "Execution time: " << (end_time - start_time) * (int)1e3 / CLOCKS_PER_SEC << " ms\n";
#endif
}
LC-91 解码方法
变种斐波那契问题 ,边界问题考虑情况比较多。
总结: 要考虑的几个点
- 首先如果是 0 开头的则一定无法进行转码
- f[0] = 1
- 判定第二个位置的取值情况
如果 s[0] + s[1] <= 26 :
如果 s[1] == 0 则只有 10 / 20 两种情况
故 f[1] = 1
否则 可以构成两种转码 f[1] = 2
否则 :
如果 s[1] == 0 : 40 / 50...等情况
f[1] = 0
否则 其单独构成一个码
f[1] = 1 - 从第三位开始:
// 如果当前位是 0 且前一位不是 1 且不是 2 那么一定不能解码成功例如00 30 故无法再往下进行
if(s[i-1] != '1' && s[i-1] != '2')
{
if(s[i] == '0')return 0;
// 如果当前不是 可以当作是单个进行解码 32 45...
else f[i] = f[i-1];
}else {
// 如果确保前一位是 1 或者 2
// 如果当前位是0 或者即只能构成 10 / 20故要从 f[i-2]进行转移
if(s[i] == '0')f[i] = f[i-2];
else {
// 否则普通的情况是s[i-1] + s[i] <= 26 可以由 f[i-1] + f[i-2]转移
if(s.substr(i-1,2) <= "26")
{
f[i] = f[i-1] + f[i-2];
}else f[i] = f[i-1];
// 否则只能进行单词拆分解码
}
}
class Solution {
public:
int numDecodings(string s) {
int n = s.length();
if(s[0] == '0')return 0;
if(n == 1)return 1;
vector<int> f(n , 0);
f[0] = 1;
if(s.substr(0 , 2) <= "26")
f[1] = s[1] == '0' ? 1 : 2;
else f[1] = s[1] == '0' ? 0 : 1;
for(int i = 2;i < n ;i ++)
{
if(s[i-1] != '1' && s[i-1] != '2')
// 如果当前位是 0 且前一位不是 1 且不是 2 那么一定不能解码成功例如00 30
// 故无法再往下进行
{
if(s[i] == '0')return 0;
// 如果当前不是 可以当作是单个进行解码 32 45...
else f[i] = f[i-1];
}else {
// 如果确保前一位是 1 或者 2
// 如果当前位是0 或者即只能构成 10 / 20故要从 f[i-2]进行转移
if(s[i] == '0')f[i] = f[i-2];
else {
// 否则普通的情况是s[i-1] + s[i] <= 26 可以由 f[i-1] + f[i-2]转移
if(s.substr(i-1,2) <= "26")
{
f[i] = f[i-1] + f[i-2];
}else f[i] = f[i-1];
// 否则只能进行单词拆分解码
}
}
}
return f[n-1];
}
};
lc-516. 最长回文子序列
对于子序列问题的通用模型就差不多是这种,但是迭代形式多种多样,由从上往下,也有从下至上,还有斜着更新状态的
状态的选定是解决dp问题的一大关键:
状态表示:
f[i][j] : 从 i - j 中的的最长回文子序列的最大长度
状态计算:
- f[i][i] = 1;
- 当 a[i] == a[j] :
f[i][j] = f[i+1][j-1] + 2
否则 f[i][j] = max(f[i+1][j],f[i][j-1])
for( i )
for (j)
if(a[i] == a[j]):
...
else ...
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n = s.size();
vector<vector<int>> f(n , vector<int> (n , 0));
// 每一个字串都是一个回文子序列
for(int i = 0;i < n ;i ++)f[i][i] = 1;
for(int i = n - 1;i >= 0; i--)
{
for(int j = i + 1;j < n ;j ++)
{
if(s[i] == s[j])
{
f[i][j] = f[i+1][j-1] + 2;
}else{
f[i][j] = max(f[i+1][j],f[i][j-1]);
}
}
}
return f[0][n-1];
}
};