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 解码方法

变种斐波那契问题 ,边界问题考虑情况比较多。

总结: 要考虑的几个点

  1. 首先如果是 0 开头的则一定无法进行转码
  2. f[0] = 1
  3. 判定第二个位置的取值情况
    如果 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
  4. 从第三位开始:
// 如果当前位是 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 中的的最长回文子序列的最大长度

状态计算:

  1. f[i][i] = 1;
  2. 当 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];
    }
};
posted @ 2020-05-15 20:42  _starsky  阅读(105)  评论(0编辑  收藏  举报