动态规划小总结

计数问题

题意 :0-n之间有多少个k

思路

​ 例如求:abcdefg中k的个数

  • 枚举带第d时候
    • abc d efg中k的数
    • 开始分类讨论,拿abc说事
      • 000-abc-1 d 000-efg
      • abc固定
        • d<k 0
        • d==k. 000-efg
        • d>k 000-999
  • 怎么处理前导0,
    • 前导0的出现:k=0时才会影响以上步骤
    • 如果k==0,枚举的时候从次高位枚举,不会出现00了
    • 此外,分类时候,000-abc-1 d 000-efg这种情况,d==0,000-abc-1不能为0

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define mst(s,_s) memset(s, _s, sizeof(s))
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;
const int N = 1e6+100;
int T,n,m;

int get_num(vector<int>nums,int l,int r)
{
    int res=0;
    for(int i=l;i>=r;i--)
        res=res*10+nums[i];
    return res;
}
int get10(int x)
{
    int res=1;
    while(x--)
    {
        res*=10;
    }
    return res;
}
// 1到n之间,有多少x
int dp(int n,int k)
{
    if(!n) return 0;
    vector<int>nums;
    while(n){
        nums.push_back(n%10);
        n/=10;
    }
    int res=0;
    
    /* abcxedg.  拿abc的大小shiu
            0 - abc-1 x  000-999
            abc 
                x<k 0
                x==k 000 edg
                x>k 000-999
        当k=0
            abc不能为0,否则0 +0 前导0
    
    */
    n=nums.size();
    for(int i=n-1-!k;i>=0;i--)
    {
        int x=nums[i];
        if(i<n-1)
        {
            res+=get_num(nums,n-1,i+1)*get10(i);
            if(k==0) res-=get10(i);
        }
        if(x>k) res+=get10(i);
        else if(x==k) res+=1+get_num(nums,i-1,0);
    }
    return res;
}


int main() {
    int l,r;
    while(cin>>l>>r)
    {
        if(!l || !r) break;
        if(l>r) swap(l,r);
        for(int i=0;i<=9;i++)
            cout<<dp(r,i)-dp(l-1,i)<<' ';
        cout<<endl;
    }
    return 0;
}

没有上司的舞会

​ 树形DP f[u] [0] 表示u为根节点不选u的最大值,f[u] [1] 表示u为根节点选u的最大值

​ 选u的话,各个子节点只能不选,不选u的话,子节点可选可不选

代码

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 6010;

int n;
int h[N], e[N], ne[N], idx;
int happy[N];
int f[N][2];
bool has_fa[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u)
{
    f[u][1] = happy[u];

    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        dfs(j);

        f[u][1] += f[j][0];
        f[u][0] += max(f[j][0], f[j][1]);
    }
}

int main()
{
    scanf("%d", &n);

    for (int i = 1; i <= n; i ++ ) scanf("%d", &happy[i]);

    memset(h, -1, sizeof h);
    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(b, a);
        has_fa[a] = true;
    }

    int root = 1;
    while (has_fa[root]) root ++ ;

    dfs(root);

    printf("%d\n", max(f[root][0], f[root][1]));

    return 0;
}

滑雪

​ 一个二维矩阵,代表一个平面的高度,从高往低滑

​ 问:最长的滑雪路径。

​ f[i] [j] 表示从这个点开始滑的最长距离

#include<iostream>
#include<cstring>
using namespace std;
const int N=1e3+100;
int f[N][N],h[N][N];
int n,m;

int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};

int dp(int a,int b)
{
    
    if(f[a][b]!=-1) return f[a][b];
    int maxv=1;
    
    for(int i=0;i<4;i++)
    {
       /// cout<<ssss<<endl;
        int na=a+dx[i],nb=b+dy[i];
        
        //cout<<na<<' '<<nb<<endl;
        if(na>=1 && na<=n && nb>=1 && nb<=m && h[na][nb] < h[a][b])
            maxv = max(maxv,dp(na,nb)+1);
    }
    return f[a][b]=maxv;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>h[i][j];
    int res=0;
    memset(f,-1,sizeof f);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
         {
            // cout<<i<<' '<<j<<endl;
             res=max(res,dp(i,j));
         }
    cout<<res<<endl;
    return 0;
}

最长上升子序列(贪心)

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
int a[N];
int q[N];

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    int len = 0;
    for (int i = 0; i < n; i ++ )
    {
        int l = 0, r = len;
        while (l < r)
        {
            int mid = l + r + 1 >> 1;
            if (q[mid] < a[i]) l = mid;
            else r = mid - 1;
        }
        len = max(len, r + 1);
        q[r + 1] = a[i];
    }

    printf("%d\n", len);

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值