动态规化

计数问题

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

思路

​ 例如求:abcdefg中k的个数

  • 枚举带第d时候
    • abc d efg中k的数
    • 开始分类讨论,拿abc说事
      • 000-abc-1 d 000-efg
      • abc固定
        • d
        • 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 
                xk 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 
#include 
#include 

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
#include
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<
        int na=a+dx[i],nb=b+dy[i];
        
        //cout<
        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<
             res=max(res,dp(i,j));
         }
    cout<<res<<endl;
    return 0;
}

最长上升子序列(贪心)

代码

#include 
#include 

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;
}

你可能感兴趣的:(acwing算法基础课)