【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\n",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\n", ans );
    for ( int i = 0; i <= k; i ++ )
        if ( dp[k][i] ) printf ( "%d ", i );
    return 0;
}

 

 

 

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

posted @ 2018-08-14 16:43  Wans_ovo  阅读(145)  评论(0编辑  收藏  举报