为神马JQ比我先发啊(QAQ)
本人蒟蒻一枚,帖子中如有错误,请在评论区指正
500分考了270,白给了75,不玩了玩不起
做个简单的总结吧。
对了,除了第 3 3 3 题,没有现成代码,不要指望复制粘贴。
考点:完全背包
为什么这么说呢?
你可以认为每种硬币的数量是无限的。
以此,我们可以判断出,这是一道完全背包。
所以,可以定义:
d p [ i ] dp[\ i\ ] dp[ i ] 表示凑出 i i i 的面值最少需要的硬币数量。
所以,我们不难推出:
dp[i]=min(dp[i],dp[i-a[j]]+1);
//显然,dp[i-a[j]]表示凑出i-a[j]的面值最少需要的硬币数量,再加上一个面值为a[j]的硬币即可凑出i的面值。
当然,只有满足 i ≥ a [ j ] i \geq a[\ j\ ] i≥a[ j ]时,上述状态转移方程用。
所以,在完成输入后,可以对 a a a 数组进行排序,方便后面判断,当 i < a [ j ] i < a[\ j\ ] i<a[ j ] ,就直接退出循环。
关于我犯的一个愚蠢错误:
题目已知:
0 ≤ a m o u n t ≤ 1 0 4 0 \leq amount \leq 10^4 0≤amount≤104
可是当我在定义数组时:
int dp[1005];
//1000=10^3
RE,白给75。
所以,检查真的很重要。
简单的说说检查要检查些什么:
考点:最长公共子序列(以下简称LCS)
这个考点应该是显然的。
但是,与常规的LCS相比,这道题要求的是 3 3 3 个序列的LCS。
其实类比常规的LCS,这题也没啥难的。
定义状态:
d p [ i ] [ j ] [ k ] dp[\ i\ ][\ j\ ][\ k\ ] dp[ i ][ j ][ k ] 表示第 1 1 1 个序列的前 i i i 个字符,第 2 2 2 个序列的前 j j j 个字符,第 3 3 3 个序列的前 k k k 个字符的LCS。
显然,当 a [ i ] = b [ j ] = c [ k ] a[\ i\ ]=b[\ j\ ]=c[\ k\ ] a[ i ]=b[ j ]=c[ k ] 时, a [ i ] a[\ i\ ] a[ i ] , b [ j ] b[\ j\ ] b[ j ] , c [ k ] c[\ k\ ] c[ k ] 都可以为答案作出贡献,所以这个时候,就可以将 d p [ i − 1 ] [ j − 1 ] [ k − 1 ] + 1 dp[\ i-1\ ][\ j-1\ ][\ k-1\ ]+1 dp[ i−1 ][ j−1 ][ k−1 ]+1 的值赋给 d p [ i ] [ j ] [ k ] dp[\ i\ ][\ j\ ][\ k\ ] dp[ i ][ j ][ k ] 。
反之,说明 a [ i ] a[\ i\ ] a[ i ] , b [ j ] b[\ j\ ] b[ j ] , c [ k ] c[\ k\ ] c[ k ] 中,至少有 1 1 1 个无法为答案作出贡献,所以,取 d p [ i − 1 ] [ j ] [ k ] dp[\ i-1\ ][\ j\ ][\ k\ ] dp[ i−1 ][ j ][ k ] , d p [ i ] [ j − 1 ] [ k ] dp[\ i\ ][\ j-1\ ][\ k\ ] dp[ i ][ j−1 ][ k ] , d p [ i ] [ j ] [ k − 1 ] dp[\ i\ ][\ j\ ][\ k-1\ ] dp[ i ][ j ][ k−1 ] 中的最大值,将其赋给 d p [ i ] [ j ] [ k ] dp[\ i\ ][\ j\ ][\ k\ ] dp[ i ][ j ][ k ] 。
这题就没什么值得注意的点了,下一道。
考点:DFS + 剪枝
这道题有多种做法,这里展示的是我的做法。(其实是考场上的代码有问题,数据加强后仅90分,改成了官方做法)
显然,对于任意一个带分数,将加号和除号去掉后,得到的是1至9的一个全排列
那么我们就有了基本思路:我们可以求出一个全排列,在判断能否将其分割成我们想要的数。具体有一些优化,结合代码来讲。
#include
int a[15],ALL;//ALL用于统计总数量
bool flag[15];
void write(int num){//快读
if(num<0){
putchar('-');
num*=-1;
}
if(num>9){
write(num/10);
}
putchar(num%10+'0');
}
int Sum(int l,int r){//将一个指定长度的数组转化成数值
int ans=0;
for(int i=r;i>=l;i--){
ans=ans*10+a[i];
}
return ans;
}
void dfs1(int a[],int num){//第二个dfs,用于创造带分数
int m1,m2,m3;
for(int i=1;i<=7;i++){//枚举带分数中整数部分的截止位置
//关于截止位置:整数部分的值为序列[1,i]转化成的数值的值
//因为分子和分母至少要有一位,所以最多到7截止
m1=Sum(1,i);//赋值
if(m1>num){//第一个剪枝:如果带分数中整数部分已经大于等于我们要求得的数
return ;//直接截止
}
for(int k=i+(9-i)/2;k<=8;k++){//枚举分母的截止位置。截止位置含义同上
//第二个剪枝
//分子分母会在序列[i+1,9]中排出,而分母必须大于分子,即分母长度必须大于等于分子长度
//所以,分母长度为(9-i)/2,在加上i,就是分母最小的截止位置
//因为分子至少要有一位,所以最多到8截止
m2=Sum(i+1,k);
m3=Sum(k+1,9);
if(m2>m3&&m2%m3==0&&m1+m2/m3==num){
ALL++;
write(num);
putchar('=');
write(m1);
putchar('+');
write(m2);
putchar('/');
write(m3);
putchar('\n');
//以上输出相当于printf("%d=%d+%d/%d\n",num,m1,m2,m3);
}
}
}
}
void dfs(int num,int n){//第一个dfs,造出全排列
if(num==10){//全排列造好了
dfs1(a,n);
}
for(int i=1;i<=9;i++){
if(flag[i]==0){
flag[i]=1;
a[num]=i;
dfs(num+1,n);
flag[i]=0;
a[num]=0;
}
}
}
int main(){
freopen("fraction.in","r",stdin);
freopen("fraction.out","w",stdout);
int n;
while(scanf("%d",&n)!=EOF){
ALL=0;
dfs(1,n);
printf("%d\n",ALL);
}
return 0;
}
其实关于全排列,STL里面有相关的函数,大家可以在here查阅更多资料。
考点:LIS + 二分
这题很明显是一道LIS的变形。
下面展示蒟蒻的做法:
我们定义:
d p [ i ] dp[\ i\ ] dp[ i ] 表示序列 [ 1 , i ] [\ 1,i\ ] [ 1,i ] 中长度最长的最长上升子序列的长度(必须包含第 i i i 个元素)。
d p 1 [ i ] dp1[\ i\ ] dp1[ i ] 表示序列 [ i , n ] [\ i,n\ ] [ i,n ] 中长度最长的最长下降子序列的长度(必须包含第 i i i 个元素)。
这题就这么简单的完成了……吗?
绝对不可能!
观察 n n n 的取值范围:
1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105
数据范围很大, O ( n 2 ) O(n^2) O(n2) 算法是过不去的,至少也要是 O ( n × log n ) O(n \times \log_n) O(n×logn) 。
关于LIS的算法,有一个优化后的算法,其时间复杂度可以从 O ( n 2 ) O(n^2) O(n2) 降到 O ( n × log n ) O(n \times \log_n) O(n×logn) 。
关于这个优化后的算法:
蒟蒻会写,但是解释不清楚
为了不误导更多人,这里不做解释(话说应该都会写吧),大家可以试试这道LIS,只能用优化后的算法才能过。
考点:递推
话说递推和线性DP有神马区别
本次测试中最难的一道。
让我们先从最难的想的状态定义入手吧:
定义:
d p [ i ] [ 0 ] [ 0 ] dp[\ i\ ][\ 0\ ][\ 0\ ] dp[ i ][ 0 ][ 0 ] 表示前 i i i 个格子中,第 i i i 个格子无烈焰,第 i + 1 i+1 i+1 个格子无烈焰的情况总数
d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ] 表示前 i i i 个格子中,第 i i i 个格子无烈焰,第 i + 1 i+1 i+1 个格子有烈焰的情况总数
d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 表示前 i i i 个格子中,第 i i i 个格子有烈焰,第 i + 1 i+1 i+1 个格子无烈焰的情况总数
d p [ i ] [ 1 ] [ 1 ] dp[\ i\ ][\ 1\ ][\ 1\ ] dp[ i ][ 1 ][ 1 ] 表示前 i i i 个格子中,第 i i i 个格子有烈焰,第 i + 1 i+1 i+1 个格子有烈焰的情况总数
为什么这样定义呢?
结合下面的状态转移方程来讲:
当第 i i i 个格子为 0 时,我们只需要处理 d p [ i ] [ 0 ] [ 0 ] dp[i][\ 0\ ][\ 0\ ] dp[i][ 0 ][ 0 ] 的值即可。
很简单,因为第 i i i 个格子为 0 ,所以第 i − 1 i-1 i−1 个格子,第 i i i 个格子和第 i + 1 i+1 i+1 上一定没有烈焰,所以只需将 d p [ i − 1 ] [ 0 ] [ 0 ] dp[\ i-1\ ][\ 0\ ][\ 0\ ] dp[ i−1 ][ 0 ][ 0 ] 的值赋给 d p [ i ] [ 0 ] [ 0 ] dp[\ i\ ][\ 0\ ][\ 0\ ] dp[ i ][ 0 ][ 0 ] 即可。
当第 i i i 个格子为 1 时,我们需要处理 d p [ i ] [ 0 ] [ 0 ] dp[\ i\ ][\ 0\ ][\ 0\ ] dp[ i ][ 0 ][ 0 ] 和 d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ]的值。
因为当第 i i i 个格子为 1 时,第 i i i 个格子必然没有烈焰,且第 i + 1 i+1 i+1 个格子和第 i − 1 i-1 i−1 个格子会有两种情况:
考虑第 1 1 1 种情况,在该情况下,第 i − 1 i-1 i−1 个格子和第 i i i 个格子都没有烈焰,第 i + 1 i+1 i+1 个格子有烈焰,所以,要将 d p [ i − 1 ] [ 0 ] [ 0 ] dp[\ i-1\ ][\ 0\ ][\ 0\ ] dp[ i−1 ][ 0 ][ 0 ] 的值赋给 d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ] 。
考虑第 2 2 2 种情况,在该情况下,第 i − 1 i-1 i−1 有烈焰,第 i i i 个格子和第 i + 1 i+1 i+1 个格子都没有烈焰,所以,要将 d p [ i − 1 ] [ 1 ] [ 0 ] dp[\ i-1\ ][\ 1\ ][\ 0\ ] dp[ i−1 ][ 1 ][ 0 ] 的值赋给 d p [ i ] [ 0 ] [ 0 ] dp[\ i\ ][\ 0\ ][\ 0\ ] dp[ i ][ 0 ][ 0 ] 。
当第 i i i 个格子为 2 时,我们只需要处理 d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ] 的值即可。
因为当第 i i i 个格子为 2 时,第 i i i 个格子必然没有烈焰,且第 i + 1 i+1 i+1 个格子和第 i − 1 i-1 i−1 个格子必然有烈焰,所以将 d p [ i − 1 ] [ 1 ] [ 0 ] dp[\ i-1\ ][\ 1\ ][\ 0\ ] dp[ i−1 ][ 1 ][ 0 ] 的值赋给 d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ] 即可。
当第 i i i 个格子为 * 时,我们需要处理 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 和 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ]的值。
因为当第 i i i 个格子为 * 时,第 i i i 个格子必然有烈焰,但第 i + 1 i+1 i+1 个格子和第 i − 1 i-1 i−1 个格子的情况我们都不知道,所以要分情况。
考虑关于 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 的状态转移方程,在该情况下,因为第 i − 1 i-1 i−1 个格子的情况我们不知道,所以将 d p [ i − 1 ] [ 0 ] [ 1 ] + d p [ i − 1 ] [ 1 ] [ 1 ] dp[\ i-1\ ][\ 0\ ][\ 1\ ]+dp[\ i-1\ ][\ 1\ ][\ 1\ ] dp[ i−1 ][ 0 ][ 1 ]+dp[ i−1 ][ 1 ][ 1 ] 取模后的值赋给 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] ,注意取模。
同理, d p [ i ] [ 1 ] [ 1 ] dp[\ i\ ][\ 1\ ][\ 1\ ] dp[ i ][ 1 ][ 1 ] 的状态转移方程我们也能推出来了,与 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 的状态转移方程一模一样。
当第 i i i 个格子为 ? 时,我们需要处理 d p [ i ] [ 0 ] [ 0 ] dp[\ i\ ][\ 0\ ][\ 0\ ] dp[ i ][ 0 ][ 0 ] , d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ], d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 和 d p [ i ] [ 1 ] [ 1 ] dp[\ i\ ][\ 1\ ][\ 1\ ] dp[ i ][ 1 ][ 1 ]的值。
因为当第 i i i 个格子为 ? 时,第 i i i 个格子,第 i + 1 i+1 i+1 个格子和第 i − 1 i-1 i−1 个格子的情况我们都不知道,所以要分情况。
考虑关于 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 的状态转移方程,在该情况下,因为第 i − 1 i-1 i−1 个格子的情况我们不知道,所以将 d p [ i − 1 ] [ 0 ] [ 1 ] + d p [ i − 1 ] [ 1 ] [ 1 ] dp[\ i-1\ ][\ 0\ ][\ 1\ ]+dp[\ i-1\ ][\ 1\ ][\ 1\ ] dp[ i−1 ][ 0 ][ 1 ]+dp[ i−1 ][ 1 ][ 1 ] 取模后的值赋给 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 。同理, d p [ i ] [ 1 ] [ 1 ] dp[\ i\ ][\ 1\ ][\ 1\ ] dp[ i ][ 1 ][ 1 ] 的状态转移方程应该与 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 的状态转移方程一模一样。
考虑关于 d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ] 的状态转移方程,在该情况下,因为第 i − 1 i-1 i−1 个格子的情况我们不知道,所以将 d p [ i − 1 ] [ 0 ] [ 0 ] + d p [ i − 1 ] [ 1 ] [ 0 ] dp[\ i-1\ ][\ 0\ ][\ 0\ ]+dp[\ i-1\ ][\ 1\ ][\ 0\ ] dp[ i−1 ][ 0 ][ 0 ]+dp[ i−1 ][ 1 ][ 0 ] 取模后的值赋给 d p [ i ] [ 1 ] [ 0 ] dp[\ i\ ][\ 1\ ][\ 0\ ] dp[ i ][ 1 ][ 0 ] 。同理, d p [ i ] [ 0 ] [ 0 ] dp[\ i\ ][\ 0\ ][\ 0\ ] dp[ i ][ 0 ][ 0 ] 的状态转移方程应该与 d p [ i ] [ 0 ] [ 1 ] dp[\ i\ ][\ 0\ ][\ 1\ ] dp[ i ][ 0 ][ 1 ] 的状态转移方程一模一样。
状态转移方程我们就推完了,考虑初始化。
因为第 1 1 1 个格子为 0 ,所以第 1 1 1 个格子和第 2 2 2 个格子必然没有烈焰,只需将 d p [ 1 ] [ 0 ] [ 0 ] dp[\ 1\ ][\ 0\ ][\ 0\ ] dp[ 1 ][ 0 ][ 0 ] 赋成 1 1 1 即可。
因为第 1 1 1 个格子为 1 ,所以第 1 1 1 个格子必然没有烈焰,第 2 2 2 个格子必然有烈焰(这个应该不用解释吧?),只需将 d p [ 1 ] [ 0 ] [ 0 ] dp[\ 1\ ][\ 0\ ][\ 0\ ] dp[ 1 ][ 0 ][ 0 ] 赋成 1 1 1 即可。
这种情况是无解的,不用管。
因为第 1 1 1 个格子为 * ,所以第 1 1 1 个格子有烈焰,第 2 2 2 个格子的情况我们不知道,所以,需要将 d p [ 1 ] [ 1 ] [ 0 ] dp[\ 1\ ][\ 1\ ][\ 0\ ] dp[ 1 ][ 1 ][ 0 ] 和 d p [ 1 ] [ 1 ] [ 1 ] dp[\ 1\ ][\ 1\ ][\ 1\ ] dp[ 1 ][ 1 ][ 1 ] 赋成 1 1 1 。
因为第 1 1 1 个格子为 ? ,所以第 1 1 1 个格子和第 2 2 2 个格子的情况我们都不知道,所以,需要将 d p [ 1 ] [ 1 ] [ 0 ] dp[\ 1\ ][\ 1\ ][\ 0\ ] dp[ 1 ][ 1 ][ 0 ] , d p [ 1 ] [ 1 ] [ 1 ] dp[\ 1\ ][\ 1\ ][\ 1\ ] dp[ 1 ][ 1 ][ 1 ], d p [ 1 ] [ 0 ] [ 1 ] dp[\ 1\ ][\ 0\ ][\ 1\ ] dp[ 1 ][ 0 ][ 1 ] 和 d p [ 1 ] [ 0 ] [ 0 ] dp[\ 1\ ][\ 0\ ][\ 0\ ] dp[ 1 ][ 0 ][ 0 ] 赋成 1 1 1 。
那么这题就完成了。