【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)$解决了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define ll long long
 6 #define RG register
 7 
 8 using namespace std;
 9 
10 const int mod = 1000000007;
11 
12 int n, m, a[1005];
13 int f[1005];
14 
15 int main ( ) {
16     freopen ( "gift.in", "r", stdin );
17     freopen ( "gift.out", "w", stdout );
18     scanf ( "%d%d", &n, &m );
19     for ( int i = 1; i <= n; i ++ ) {
20         scanf ( "%d", &a[i] );
21     }
22     sort ( a + 1, a + 1 + n );
23     ll ans = 0; int sum = 0;
24     for ( RG int i = 1; i <= n; i ++ ) {
25         memset ( f, 0, sizeof ( f ) );
26         f[0] = 1;
27         for ( RG int k = i + 1; k <= n; k ++ ) {
28             if ( m - sum < a[k] ) break;
29             for ( RG int j = m - sum; j >= a[k]; j -- ) {
30                 f[j] = ( f[j] + f[j-a[k]] ) % mod;
31             }
32         }
33         for ( RG int j = max ( m - sum - a[i] + 1, 0 ); j <= m - sum; j ++ )
34             ans = ( ans + f[j] ) % mod;
35         sum += a[i];
36     }
37     printf ( "%d", ans );
38     return 0; 
39 }

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

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 int main ( ) {
 6     freopen ( "fseq.in", "r", stdin );
 7     freopen ( "fseq.out", "w", stdout );
 8     int T;
 9     scanf ( "%d", &T );
10     while ( T -- ) {
11         int n, m;
12         scanf ( "%d%d", &n, &m );
13         if ( n < m ) printf ( "0.000000\n" );
14         else printf ( "%.6lf", ( double ) ( n - m + 1 ) / ( double ) ( n + 1 ) );
15     }
16     return 0;
17 }

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

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

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

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

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

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 
 7 ll dp[20][2][20];
 8 int num[20], fi[20];
 9 
10 ll dfs ( int dep, int up, int tot ) {
11     if ( dp[dep][up][tot] ) return dp[dep][up][tot];
12     if ( !dep && tot ) return 1;
13     if ( !dep ) return 0;
14     int MA = up ? num[dep] : 9;
15     ll res = 0;
16     for ( int i = 0; i <= MA; i ++ ) {
17         if ( i == 0 && !tot ) res += dfs ( dep - 1, up && i == MA,tot );
18         else if ( fi[tot + 1] != i ) {
19             fi[dep] = i;
20             res += dfs ( dep - 1, up && i == MA, tot + 1 );
21             fi[dep] = -1;
22         }
23     }
24     dp[dep][up][tot] = res;
25     return res;
26 }
27 
28 ll work ( ll x ) {
29     int cnt = 0;
30     memset ( fi, -1, sizeof ( fi ) );
31     memset ( dp, 0, sizeof ( dp ) );
32     while ( x ) {
33         num[++cnt] = x % 10;
34         x /= 10;
35     }
36     return dfs ( cnt, 1, 0 );
37 }
38 
39 int main ( ) {
40     freopen ( "lucky.in", "r", stdin );
41     freopen ( "lucky.out", "w", stdout );
42     ll x, y;
43     scanf ( "%I64d%I64d", &x, &y );
44     ll xx = work ( x - 1 ), yy = work ( y );
45     printf ( "%I64d", yy - xx );
46 }
// wans
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 ll l,r,dp[12][2][2];
 6 int tot,dig[30];
 7 bool vis[12][2][2];
 8 
 9 ll dfs(int dep,bool lf_up,bool rg_up) {
10     
11     if(vis[dep][lf_up][rg_up]) return dp[dep][lf_up][rg_up];
12     if(dep == tot - dep + 1) {
13         if(lf_up && rg_up) return dig[dep];
14         else if(! lf_up) return 10;
15         else if(lf_up) return dig[dep] + 1;
16     }
17     if(dep > tot - dep + 1) {
18         if(lf_up && rg_up) return 0;
19         return 1;
20     }
21     vis[dep][lf_up][rg_up] = true;
22     int up = lf_up ? dig[tot - dep + 1] : 9;
23     ll res = 0;
24     for(int i = 0;i <= up;i ++)
25       for(int j = 0;j <= 9;j ++) {
26           if(i == j) continue;
27           if(dep == 1 && i == 0) continue;
28           bool upup;
29           if(j > dig[dep]) upup = true;
30           else if(j < dig[dep]) upup = false;
31           else upup = rg_up;
32           res += dfs(dep + 1,lf_up && (i == dig[tot - dep + 1]),upup);
33       }
34     return dp[dep][lf_up][rg_up] = res;
35 }
36 
37 ll solve(ll s) {
38     
39     memset(vis,0,sizeof(vis));
40     ll ss = s,ans = 0;
41     tot = 0;
42     while(s) {
43         dig[++ tot] = s % 10;
44         s /= 10;
45     }
46     ans += dfs(1,1,0);
47     for(int i = 1;i <= tot;i ++) dig[i] = 9;
48     for(tot = tot - 1;tot >= 1;tot --) {
49         memset(vis,0,sizeof(vis));
50         ans += dfs(1,1,0);
51     }
52     return ans;
53 }
54 
55 int main( ) {
56     
57     freopen("lucky.in","r",stdin);
58     freopen("lucky.out","w",stdout);
59     scanf("%I64d%I64d",& l,& r);
60     ll ans1 = solve(l - 1);
61     ll ans2 = solve(r);
62     printf("%I64d",ans2 - ans1);
63 }
//yuli

猜你喜欢

转载自www.cnblogs.com/wans-caesar-02111007/p/9501437.html