【8.19校内测试】【背包】【卡特兰数】【数位dp】

早上随便搞搞t1t3就开始划水了,t2一看就是组合数学看着肚子疼...结果t1t3都a了??感天动地。

从小到大排序,从前到后枚举i,表示i是整个背包中不选的物品中代价最小的那个,即i不选,1到i-1全部都要选,i+1到n做背包(此时容量为m-pre),极限复杂度$O(n^3)$,可是我们在中间判断一下,当剩余容量比当前i代价小,break。可以减掉很大的复杂度!(cena评测最慢0.04s~

或者可以在枚举i时倒着枚举,每次背包就可以$O(n)$解决了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define RG register

using namespace std;

const int mod = 1000000007;

int n, m, a[1005];
int f[1005];

int main ( ) {
    freopen ( "gift.in", "r", stdin );
    freopen ( "gift.out", "w", stdout );
    scanf ( "%d%d", &n, &m );
    for ( int i = 1; i <= n; i ++ ) {
        scanf ( "%d", &a[i] );
    }
    sort ( a + 1, a + 1 + n );
    ll ans = 0; int sum = 0;
    for ( RG int i = 1; i <= n; i ++ ) {
        memset ( f, 0, sizeof ( f ) );
        f[0] = 1;
        for ( RG int k = i + 1; k <= n; k ++ ) {
            if ( m - sum < a[k] ) break;
            for ( RG int j = m - sum; j >= a[k]; j -- ) {
                f[j] = ( f[j] + f[j-a[k]] ) % mod;
            }
        }
        for ( RG int j = max ( m - sum - a[i] + 1, 0 ); j <= m - sum; j ++ )
            ans = ( ans + f[j] ) % mod;
        sum += a[i];
    }
    printf ( "%d", ans );
    return 0; 
}

题意是要求该序列-1的累加和永远小于等于1的累加和的概率。经典的卡特兰数问题,在坐标系中,可以把-1看成向上走,把1看成向右走,最终目标是计算从原点走到$(n,m)$并且过程中不能超出到$y=x$这条直线的方案数。方案数为$C_{m+n}^m-C_{m+n}^{m-1}$,即$frac{n-m+1}{n+1}$

#include<iostream>
#include<cstdio>
using namespace std;

int main ( ) {
    freopen ( "fseq.in", "r", stdin );
    freopen ( "fseq.out", "w", stdout );
    int T;
    scanf ( "%d", &T );
    while ( T -- ) {
        int n, m;
        scanf ( "%d%d", &n, &m );
        if ( n < m ) printf ( "0.000000
" );
        else printf ( "%.6lf", ( double ) ( n - m + 1 ) / ( double ) ( n + 1 ) );
    }
    return 0;
}

感觉我的方法是碰巧遇到可以过的类型了...如果题目不合法的对应关系改一下马上就会挂。

但是题目给的是对应不能相同嘛~我定义的$dp[dep][up][tot]$分别表示当前数的位置,是否顶上界,已经填了多少个数(抛开前导零,记忆化的时候会发现,除了顶上界的情况只会计算一次并且不会第二次返回,不顶上界的情况计算一次后每次都直接返回了,不管前面填的什么数和后面将填什么数...

可是对于这道题它恰好就是对的!在不顶上界的情况,所有数字都可以填,并且所有数字都有相同的不合法情况个数!所以直接记忆化就没有问题...

可是$yuli$dalao(%%%指出,只要把题稍微改一改,比如对应位置不能同时为质数之类的...每个数的方案数就不一样了!

所以正解是枚举数的长度,从前后同时填数即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;

ll dp[20][2][20];
int num[20], fi[20];

ll dfs ( int dep, int up, int tot ) {
    if ( dp[dep][up][tot] ) return dp[dep][up][tot];
    if ( !dep && tot ) return 1;
    if ( !dep ) return 0;
    int MA = up ? num[dep] : 9;
    ll res = 0;
    for ( int i = 0; i <= MA; i ++ ) {
        if ( i == 0 && !tot ) res += dfs ( dep - 1, up && i == MA,tot );
        else if ( fi[tot + 1] != i ) {
            fi[dep] = i;
            res += dfs ( dep - 1, up && i == MA, tot + 1 );
            fi[dep] = -1;
        }
    }
    dp[dep][up][tot] = res;
    return res;
}

ll work ( ll x ) {
    int cnt = 0;
    memset ( fi, -1, sizeof ( fi ) );
    memset ( dp, 0, sizeof ( dp ) );
    while ( x ) {
        num[++cnt] = x % 10;
        x /= 10;
    }
    return dfs ( cnt, 1, 0 );
}

int main ( ) {
    freopen ( "lucky.in", "r", stdin );
    freopen ( "lucky.out", "w", stdout );
    ll x, y;
    scanf ( "%I64d%I64d", &x, &y );
    ll xx = work ( x - 1 ), yy = work ( y );
    printf ( "%I64d", yy - xx );
}
// wans
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll l,r,dp[12][2][2];
int tot,dig[30];
bool vis[12][2][2];

ll dfs(int dep,bool lf_up,bool rg_up) {
    
    if(vis[dep][lf_up][rg_up]) return dp[dep][lf_up][rg_up];
    if(dep == tot - dep + 1) {
        if(lf_up && rg_up) return dig[dep];
        else if(! lf_up) return 10;
        else if(lf_up) return dig[dep] + 1;
    }
    if(dep > tot - dep + 1) {
        if(lf_up && rg_up) return 0;
        return 1;
    }
    vis[dep][lf_up][rg_up] = true;
    int up = lf_up ? dig[tot - dep + 1] : 9;
    ll res = 0;
    for(int i = 0;i <= up;i ++)
      for(int j = 0;j <= 9;j ++) {
          if(i == j) continue;
          if(dep == 1 && i == 0) continue;
          bool upup;
          if(j > dig[dep]) upup = true;
          else if(j < dig[dep]) upup = false;
          else upup = rg_up;
          res += dfs(dep + 1,lf_up && (i == dig[tot - dep + 1]),upup);
      }
    return dp[dep][lf_up][rg_up] = res;
}

ll solve(ll s) {
    
    memset(vis,0,sizeof(vis));
    ll ss = s,ans = 0;
    tot = 0;
    while(s) {
        dig[++ tot] = s % 10;
        s /= 10;
    }
    ans += dfs(1,1,0);
    for(int i = 1;i <= tot;i ++) dig[i] = 9;
    for(tot = tot - 1;tot >= 1;tot --) {
        memset(vis,0,sizeof(vis));
        ans += dfs(1,1,0);
    }
    return ans;
}

int main( ) {
    
    freopen("lucky.in","r",stdin);
    freopen("lucky.out","w",stdout);
    scanf("%I64d%I64d",& l,& r);
    ll ans1 = solve(l - 1);
    ll ans2 = solve(r);
    printf("%I64d",ans2 - ans1);
}
//yuli
原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9501437.html