zoukankan      html  css  js  c++  java
  • 【8.14校内测试】【DP专题】

    nlogn做法,dp[i]表示当前长度为i的最长上升子序列末尾元素的值。

    不会写lower_bound(qwq,贴一个以前的好看点的代码

    #include<iostream>//使用lower_bound()函数 
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    int a[30005],d[30005],n,len=0;
    int main()
    {
      scanf("%d",&n);
      for (int i=1;i<=n;i++)
         scanf("%d",&a[i]);
      d[++len]=a[1];
      for (int i=2;i<=n;i++)
       if (a[i]>d[len])d[++len]=a[i];
       else
       {
          int x=lower_bound(d+1,d+len+1,a[i])-d;
          d[x]=a[i];
       }
      printf("%d
    ",len);
      return 0;
    }

    法一:用度数计算,可以证明最后答案等于选出的k个点的总度数-2*(k-2)-2,表示选择的一条链上去掉两头每个点少减两个度数,两头少减一个度数,就算有分叉也不影响。然后题目转换为求连续的k个点使他们的度数和最小。定义dp[u][k]表示以u为根的子树中选择k个点能获得的最小度数和(包括自己。背包转移。

    大佬zyl的代码:

    #include<stdio.h>
    #include<cstring>
    
    const int MAXN = 155;
    
    inline int min(int a, int b) {
        return a < b ? a : b;
    }
    
    struct Edge {
        int to;
        Edge *nxt;
    } pool[MAXN * 2], *head[MAXN], *tail = pool;
    
    inline void addEdge(int from, int to) {
        Edge *it = tail++;
        it -> to = to;
        it -> nxt = head[from], head[from] = it;
    }
    
    int n, k, ans;
    int dp[MAXN][MAXN], size[MAXN], w[MAXN];
    
    void dfs(int u, int fa) {
        dp[u][0] = 0, dp[u][1] = w[u], size[u] = 1;
        for(Edge *it = head[u]; it; it = it -> nxt) {
            int v = it -> to;
            if(v == fa)        continue;
            dfs(v, u);
            for(int i = size[u]; i >= 1; i--)
                for(int j = 0; j <= size[v] && i + j <= k; j++)
                    if(i + j != 1)
                        dp[u][i + j] = min(dp[u][i + j], dp[u][i] + dp[v][j]);
            size[u] += size[v];
        }
        
        ans = min(ans, dp[u][k]);
    }
    
    int main() {
        freopen("isolate.in", "r", stdin);
        freopen("isolate.out", "w", stdout);
        
        scanf("%d%d", &n, &k);
        for(int i = 1; i < n; i++) {
            int u, v; scanf("%d%d", &u, &v);
            addEdge(u, v), addEdge(v, u);
            w[u]++, w[v]++;
        }
        
        memset(dp, 127 / 3, sizeof(dp));
        ans = 0x7fffffff;
        
        dfs(1, 1);
        
        printf("%d", ans - k * 2 + 2);
    }

    法二:定义dp[u][k]表示以u为根的子树中选择k个点最少需要割掉的边。同样是背包转移,如果儿子不选,dp[u][j]就要加一。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    int n, k;
    
    const int N = 155;
    
    int stot, tov[N*2], nex[N*2], h[N];
    void add ( int u, int v ) {
        tov[++stot] = v;
        nex[stot] = h[u];
        h[u] = stot;
    }
    
    int siz[N], dp[N][N];
    
    void dfs ( int u, int f ) {
        siz[u] = 1;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( v == f ) continue;
            dfs ( v, u );
            siz[u] += siz[v];
        }
        dp[u][1] = 0;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( v == f ) continue;
            for ( int j = min ( k, siz[u] ); j; j -- ) {
                dp[u][j] = dp[u][j] + 1;
                for ( int p = 1; p <= min ( j, siz[v] ); p ++ ) {
                    dp[u][j] = min ( dp[u][j], dp[v][p] + dp[u][j-p] );
                }
            }
        }
    }
    
    int main ( ) {
        freopen ( "isolate.in", "r", stdin );
        freopen ( "isolate.out", "w", stdout );
        scanf ( "%d%d", &n, &k );
        for ( int i = 1; i < n; i ++ ) {
            int u, v;
            scanf ( "%d%d", &u, &v );
            add ( u, v );
            add ( v, u );
        }
        memset ( dp, 0x3f3f3f3f, sizeof ( dp ) );
        dfs ( 1, 1 );
        int ans = dp[1][k];
        for ( int i = 2; i <= n; i ++ )
            ans = min ( ans, dp[i][k] + 1 );
        printf ( "%d", ans );
        return 0;
    }

    真的难受...以前写过当时秒掉的题,今天调了两个小时打死调不出来...【注意!!】像这种求方案数的状压dp通常是需要转移不放的情况的!!少了两句话直接就爆零了...

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    int r, c;
    long long dp[2][1<<12];
    
    int main ( ) {
        freopen ( "domino.in", "r", stdin );
        freopen ( "domino.out", "w", stdout );
        scanf ( "%d%d", &r, &c );
        int now = 0;
        int l = max ( r, c ), p = min ( r, c );
        r = p, c = l;
        int tot = ( 1 << r ) - 1;
        dp[now][tot] = 1;
        int pre = ( 1 << ( r - 1 ) );
        for ( int i = 1; i <= c; i ++ )
            for ( int j = 1; j <= r; j ++ ) {
                now ^= 1;
                memset ( dp[now], 0, sizeof ( dp[now] ) );
                for ( int s = 0; s <= tot; s ++ ) {
                    int up = s & pre;
                    int las = s & 1;
                    if ( !up ) {
                        int ss = s << 1 | 1;
                        dp[now][ss] += dp[now^1][s];
                    }
                    if ( up && !las ) {
                        if ( j != 1 ) {
                            int ss = ( s ^ pre ) << 1 | 3;
                            dp[now][ss] += dp[now^1][s];    
                        }
                        int ss = ( s ^ pre ) << 1;
                        dp[now][ss] += dp[now^1][s];
                    }
                    if ( up && las ) {
                        int ss = ( s ^ pre ) << 1;
                        dp[now][ss] += dp[now^1][s];
                    }
                }
            }
        printf ( "%I64d", dp[now][tot] );
        return 0;
    }

    实际上是一道比较简单的背包了...定义dp[k][p]表示子集s的和为k时s2的值为p的状态能否到达。如果dp[j][p]为true,dp[j+c[i]][p+c[i]]和dp[j+c[i]][p]都为true。【注意】当p>j时,dp[j+c[i]][p]依然需要转移!

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    int n, k, c[505];
    int dp[505][505];
    
    int main ( ) {
        freopen ( "coin.in", "r", stdin );
        freopen ( "coin.out", "w", stdout );
        scanf ( "%d%d", &n, &k );
        for ( int i = 1; i <= n; i ++ )
            scanf ( "%d", &c[i] );
        dp[0][0] = 1;
        for ( int i = 1; i <= n; i ++ ) {
            for ( int j = k-c[i]; j >= 0; j -- )
                for ( int p = k; p >= 0; p -- ) {
                    if(p<=k-c[i])
                        dp[j+c[i]][p+c[i]] |= dp[j][p];
                    dp[j+c[i]][p] |= dp[j][p];
                }
        }
        int ans = 0;
        for ( int i = 0; i <= k; i ++ )
            if ( dp[k][i] ) ans ++;
        printf ( "%d
    ", ans );
        for ( int i = 0; i <= k; i ++ )
            if ( dp[k][i] ) printf ( "%d ", i );
        return 0;
    }

    可能是今天考了四道题的原因,做的过程中有点慌,一直死扣t2和t3导致最后没有时间看t4,t3这种类型下次一定不能再错了!!

  • 相关阅读:
    查看lwjgl常用状态的值
    微信公众号开发java框架:wx4j(MenuUtils篇)
    微信公众号开发java框架:wx4j(KefuUtils篇)
    微信公众号开发java框架:wx4j(MaterialUtils篇)
    微信公众号开发java框架:wx4j(入门篇)
    hashcode和equals方法小记
    https单向认证和双向认证区别
    java开发中获取路径的一些方式
    iOS使用sqlite3原生语法进行增删改查以及FMDB的使用
    IOS自动布局
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9475555.html
Copyright © 2011-2022 走看看