18.8.19 考试总结
Gift
【问题描述】
人生赢家老王在网上认识了一个妹纸,然后妹纸的生日到了,为了表示自己的心意,他决定送她礼物。
可是她喜爱的东西特别多,然而他的钱数有限,因此他想知道当他花一定钱数后剩余钱数无法再购买任何一件剩余物品
(每种物品他最多买一个)时有多少种方案,两种方案不同,当且仅当两种方案中至少有一件品不
同,可是由于他忙着准备泡下一个妹纸(chi),因此麻烦聪明的你帮帮忙。
【输入格式】
输入第一行 n 和m,n 表示妹纸喜欢的礼物数目,m 表示现有的钱数,第二行n
个数,表示n 个物品的价格。
【输出格式】
输出一行一个数表示方案数目,答案对 1000000007 取模。
emmm这道题就是一个dp...然而蒟蒻本人并没有调出来 想到了枚举第一个不选的 然饿自作多情的写了一个类似消失的物品的垃圾dp
朴素的方法就是纯暴力 枚举每一种情况 判断剩余的钱数够不够买剩余最小的物品 如果买不起就是合法的
那么dp状态就很好定义了 dp[ i ][ j ]表示花费了i的重量不选物品中最小的是第j个的合法方案
那么要怎么轻松地get到j不选呢 排一个序 如果不选中最小的为j号 那么1 - j - 1都是选了的
所以对剩下的物品 剩余容量(m - sum)跑一个朴素背包就行了 sum就是1 - j - 1的前缀重量和
答案就是对于每一次的背包都加上 $\sum_{m - sum - w[i] + 1}^{m}f[i]$就可以了
但是这样做复杂度是n3的 那么怎么优化呢 每次我们再搞的时候都重新跑了一次背包 但是这样子就重复很多次
所以我们就倒着做 每次新加入一个物品 跑一边O(n)背包就可以了
然而我这个是一个垃圾n3的 将就看看
代码
#include <bits/stdc++.h> using namespace std; const int N = 1000 + 5; const int mod = 1e9 + 7; int dp[N],n,m,w[N],ans,f[N],sum; bool vis[N]; 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",& w[i]); sum += w[i]; } if(sum <= m) { printf("1"); return 0; } sort(w + 1,w + n + 1); sum = 0; for(int t = 1;t <= n;t ++) { int mm = m - sum; memset(f,0,sizeof(f)); if(mm < 0) break; f[0] = 1; for(int i = t + 1;i <= n;i ++) for(int j = mm;j >= w[i];j --) ] = (f[j - w[i]] + f[j]) % mod; for(int i = mm;i > mm - w[t];i --) if(i == -1) break; else ans = (ans + f[i]) % mod; sum += w[t]; } printf("%d",ans); }
Fseq
【问题描述】
一个长度为N+M 的数列,里面有N 个+1,M 个-1,如果一个这样的数列被称作F 序列(Fadeness) ,
当且仅当它的任意前缀和均非负。
for example :
1,-1,1,1,-1 is a Fadeness
1,-1,-1,1,1 is not because S(3) <0
求一个数列是Fadensee 的概率。
【输入格式】
第一行,Test , 表示测试数据的组数。
每个数据 有两个数N,M
【输出格式】
对于每组数据,输出一个实数(保留到小数点后6位)
这就是一道卡特兰数的题 把1当做向右走 把-1当做向上走
然后答案就是$C_{n +m}^{n} - C_{n + m}^{n + 1}$
化简得$\frac{n - m + 1}{n + 1}$
代码
#include <bits/stdc++.h> using namespace std; int T; double n,m; int main( ) { freopen("fseq.in","r",stdin); freopen("fseq.out","w",stdout); scanf("%d",& T); while(T --) { scanf("%lf%lf",& n,& m); if(m > n) { printf("0.000000\n"); continue; } printf("%.6lf\n",(n - m + 1)/(n + 1)); } }
Lucky
【问题描述】
DY 搞到了很多奥运会的门票,每张门票上都标了一个正整数,而DY 弄到的票刚好是从A.........B.
DY 定义一个数字是Lucky 的,当且仅当 第I 位<>第N-I+1 位 for I<>N-I+1( N 为这个数字的位数)
for example :
1234567 is lucky
1234561 is not lucky
DY 想知道他的票有多少是Lucky 的.
因此他为了考验你的智商,因此把这个问题交给了你.
【输入格式】
两个正整数,A,B
【输出格式】
A..B 中有多少数是Lucky 的
一道数位dp的题 只不过是从两头向中间搞 这样子做比较有普遍性
有人方法是直接搞 刚开始还以为是错的 结果发现每一个状态他都能对应一个的状态
所以dp是对的 但是还是两头搞比较好
代码
#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); }
感觉做题又开始没状态了 难受