T1:消失之物
题干:
$ftiasch$ 有 $N$ 个物品, 体积分别是 $W_1, W_2, ..., W_N$。 由于她的疏忽, 第 $i$ 个物品丢失了。 “要使用剩下的 $N - 1$ 物品装满容积为 $x$ 的背包,有几种方法呢?” -- 这是经典的问题了。
她把答案记为 $Count(i, x)$ ,想要得到所有 $1 <= i <= N$, $1 <= x <= M$ 的 $Count(i, x)$ 表格。
输入格式:
第 $1$ 行:两个整数 $N$ $(1 ≤ N ≤ 2 × 10^3)$ 和 $M$ $(1 ≤ M ≤ 2 × 10^3)$,物品的数量和最大的容积。
第 $2$ 行: $N$ 个整数 $W_1, W_2, ..., W_N$ , 物品的体积。
输出格式: 一个 $N × M$ 的矩阵, $Count(i, x)$ 的末位数字
题解:
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<cstdlib>
4 #define $ 3111
5 #define int long long
6 using namespace std;
7 int m,n,k,t,dp[$],v[$],g[$];
8 signed main(){
9 scanf("%lld%lld",&n,&m);
10 for(register int i=1;i<=n;++i) scanf("%lld",&v[i]);
11 dp[0]=1;
12 for(register int i=1;i<=n;++i)
13 for(register int j=m;j>=v[i];--j) dp[j]=(dp[j]+dp[j-v[i]])%10;
14 for(register int i=1;i<=n;++i){
15 for(register int j=0;j<=m;++j) g[j]=dp[j]%10;
16 for(register int j=v[i];j<=m;++j) g[j]=(g[j]-g[j-v[i]])%10;
17 for(register int j=1;j<=m;++j)
18 printf("%lld",(g[j]%10+10)%10); puts("");
19 }
20 }
T2:方伯伯的玉米田
题干:
方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美。
这排玉米一共有 $N$ 株,它们的高度参差不齐。
方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列。
方伯伯可以选择一个区间,把这个区间的玉米全部拔高 $1$ 单位高度,他可以进行最多 $K$ 次这样的操作。拔玉米则可以随意选择一个集合的玉米拔掉。
问能最多剩多少株玉米,来构成一排美丽的玉米。
输入格式:
第 $1$ 行包含 $2$ 个整数 $n$,$K$,分别表示这排玉米的数目以及最多可进行多少次操作。
第 $2$ 行包含n个整数,第 $i$ 个数表示这排玉米,从左到右第 $i$ 株玉米的高度 $a_i$。
输出格式:输出 $1$ 个整数,最多剩下的玉米数。
题解:
这道题有一个小贪心:若在 x 号节点使用操作,让 n 号节点作为右端点一定不劣。
(若右端点不在 n 节点处,而在 y 节点处,那么)
这道题一开始只能想到 $\Theta(n^2k^2)$ 的暴力,交上去才发现一个小测试点也没有。。。
在 $\Theta(n^2k^2)$ 的暴力中,有一个十分显然的 dp 式:(边转移边更新答案)
$\sum\limits_{i=1}^{n}\sum\limits_{j=0}^{k}dp[i][j]=\sum\limits_{l=1}^{i-1}\sum\limits_{r=0}^{j-1}\max(dp[l][r])+1$
我们发现在等式右侧,我们找的是一个矩阵的最大值,可以想到用二维树状数组来维护。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #define int long long
4 #define $ 505
5 using namespace std;
6 int n,k,dp[$*20][$],a[$*20],tr[$*20][$],ans,maxx;
7 inline int max(int x,int y){ return x>y?x:y; }
8 inline void add(int x,int y,int add){
9 for(register int i=x;i<=maxx+k;i+=i&(-i))
10 for(register int j=y;j<=k+1;j+=j&(-j)) tr[i][j]=max(tr[i][j],add);
11 }
12 inline int ask(int x,int y,int ans=0){
13 for(register int i=x;i>=1;i-=i&(-i))
14 for(register int j=y;j>=1;j-=j&(-j)) ans=max(tr[i][j],ans);
15 return ans;
16 }
17 signed main(){
18 scanf("%d%d",&n,&k);
19 for(register int i=1;i<=n;++i)
20 scanf("%d",&a[i]), maxx=max(maxx,a[i]);
21 for(register int i=1;i<=n;++i){
22 for(register int j=k;j>=0;--j){
23 dp[i][j]=ask(a[i]+j,j+1)+1, ans=max(ans,dp[i][j]);
24 add(a[i]+j,j+1,dp[i][j]);
25 }
26 }
27 printf("%d\n",ans);
28 }
1 #include<cstdio>
2 #include<cstring>
3 #define int long long
4 #define $ 501
5 using namespace std;
6 int n,k,dp[$*20][$],a[$*20],tr[$*21][$],ans,maxx;
7 inline int max(int x,int y){ return x>y?x:y; }
8 inline void add(int x,int y,int add){
9 for(register int i=x;i<=maxx+k;i+=i&(-i))
10 for(register int j=y;j<=k+1;j+=j&(-j)) tr[i][j]=max(tr[i][j],add);
11 }
12 inline int ask(int x,int y,int ans=0){
13 for(register int i=x;i>=1;i-=i&(-i))
14 for(register int j=y;j>=1;j-=j&(-j)) ans=max(tr[i][j],ans);
15 return ans;
16 }
17 signed main(){
18 scanf("%d%d",&n,&k);
19 for(register int i=1;i<=n;++i)
20 scanf("%d",&a[i]), maxx=max(maxx,a[i]);
21 for(register int i=1;i<=n;++i){
22 for(register int j=0;j<=k;++j)
23 dp[i][j]=ask(a[i]+j,j+1)+1, ans=max(ans,dp[i][j]);
24 for(register int j=0;j<=k;++j) add(a[i]+j,j+1,dp[i][j]);
25 }
26 printf("%d\n",ans);
27 }
T3:拦截导弹
题干:
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。
我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。
输入格式:
第一行包含一个正整数 $n$,表示敌军导弹数量;
下面 $n$ 行按顺序给出了敌军所有导弹信息:
第 $i+1$ 行包含 $2$ 个正整数 $h_i$ 和 $v_i$ ,分别表示第 $i$ 枚导弹的高度和速度。
输出格式:
第一行为一个正整数,表示最多能拦截掉的导弹数量;
第二行包含 $n$ 个 $0$ 到 $1$ 之间的实数,第 $i$ 个数字表示第 $i$ 枚导弹被拦截掉的概率(你可以保留任意多位有效数字)。
题解:
Code:
T1:
题干:
题解:
Code:
T1:
题干:
题解:
Code:
T1:
题干:
题解:
Code: