进度一览
2019牛客暑期NOIP真题班提高组7-图论
2019牛客暑期NOIP真题班普及组3-枚举1
2019牛客暑期NOIP真题班提高组6-树
2019牛客暑期NOIP真题班提高组5-DFS
2019牛客暑期NOIP真题班普及组2-模拟2
2019牛客暑期NOIP真题班提高组4-贪心
2019牛客暑期NOIP真题班提高组3-数学
2019牛客暑期NOIP真题班普及组1-模拟1
2019牛客暑期NOIP真题班提高组2-二分
2019牛客暑期NOIP真题班提高组1-模拟
2019牛客暑期NOIP真题班普及组10-DP2
2019牛客暑期NOIP真题班普及组9-DP1
2019牛客暑期NOIP真题班普及组8-贪心
2019牛客暑期NOIP真题班普及组7-数学
2019牛客暑期NOIP真题班普及组6-排序
2019牛客暑期NOIP真题班普及组5-字符串处理
2019牛客暑期NOIP真题班提高组10-DP3
2019牛客暑期NOIP真题班提高组9-DP2
2019牛客暑期NOIP真题班普及组4-枚举2
2019牛客暑期NOIP真题班提高组8-DP1
听课目标:
- 训练自己的专注程度,2x要求跟上思路,注意力高度集中
- 我并不希望这么好的参考题你是按照yxc的步伐一步一步做出来的,首先自己读题,自己思考,自己打代码。如果遇到瓶颈再看他的思路,然后自己写代码,如果实现起来实在有问题,标注该算法,然后课下自己搞。
- 检验这堂课是否有成效的标准是:此题的整个思维模型,数据范围及算法的推导,代码实现,在考场上我能够完全思考到位嘛
9.28 PJ DP1
数字游戏
prework:
-
环形DP->区间DP(数组开大两倍)
-
数学意义下的取模需要变负为正再取模
-
状态表示:
f[L][R][i]
LR的区间选了i组的乘积最大值,同样的,`g[L][R][i]`LR的区间选了i组的乘积最小值- 集合:
-
状态转移:
首先考虑f[l][r][i]
如何计算,关键是寻找“集合划分的依据”,划分依据一般选取“最后一步的操作”,所以这里我们可以按最后一部分的位置来将f[l][r][i]
所表示的所有方案划分成若干类:
最后一段是sum[l+i-1...r]
的所有划分方案;
最后一段是sum[l+i...r]
的所有划分方案;
...
最后一段是sum[r...r]
的所有划分方案;
如果最后一段是sum[k...r]
,
那么这一类的最大值是 f[l][k-1][i-1]*sum[k~r]
,其中sum()计算某一段数的和模10的余数。
最终枚举所有长度是n的区间,取最大值/最小值即可。
//2019/09/28
const int N=110,M=10;
int f[N][N][M],g[N][N][M];
int a[N],sum[N];
int n,m;
inline int get_sum(int l,int r){
int res=sum[r]-sum[l-1];
while(res<0)res+=10;
return res%10;
}
int main(){
rd(n),rd(m);
rep(i,1,n){
rd(a[i]);
a[i+n]=a[i];
}
rep(i,1,2*n)sum[i]=sum[i-1]+a[i];
rep(i,1,m)
rep(len,i,n)
rep(l,1,n){
int r=l+len-1;
if(i==1)f[l][r][i]=g[l][r][i]=get_sum(l,r);
else{
f[l][r][i]=INT_MIN;
g[l][r][i]=INT_MAX;
rep(k,l+i-1,r){
int t=get_sum(k,r);
f[l][r][i]=max(f[l][r][i],f[l][k-1][i-1]*t);
g[l][r][i]=min(g[l][r][i],g[l][k-1][i-1]*t);
}
}
}
int maxx=INT_MIN,minn=INT_MAX;
rep(i,1,n){
maxx=max(maxx,f[i][i+n-1][m]);
minn=min(minn,g[i][i+n-1][m]);
}
printf("%d
%d
",minn,maxx);
return 0;
}
开心的金明
01背包裸题;
- 总钱数相当于背包总容量;
- 每件物品的价格相当于体积;
- 每件物品的价格乘以重要度相当于价值;
守望者的逃离
首先比较跑步和放技能哪个更快:
- 跑步:平均一秒 1717 米
- 放技能:平均2.5秒恢复10点魔法,再加一秒闪烁时间,一共是3.5秒可以移动60米, 所以平均每秒 17又1/7米
所以放技能稍快一些。
因此当我们有充足的放技能时间时,一定要放技能,所以只有最后一小段没时间放技能的时候,才会用跑步的方式。
求解:
用f[i]
表示用i的时间,最多可以跑多远。
先求出只用闪烁技能时,每秒最多可以跑多远。
再用跑步的方式来“插缝”,递推出结合两种方式时,每秒最多可以跑多远。
//2019/09/28
const int N=300010;
int B,S,T;
int f[N];
int main(){
rd(B),rd(S),rd(T);//B:蓝,S:需要逃跑的距离,T:仅剩的时间
rep(i,1,T){
if(B>=10){
B-=10;//掉蓝
f[i]=f[i-1]+60;
}
else {
B+=4;//回蓝,这时站住不动
f[i]=f[i-1];
}
}
rep(i,1,T){
f[i]=max(f[i],f[i-1]+17);
}
if(f[T]>=S){
rep(i,1,T){
if(f[i]>=S){
puts("Yes");
printf("%d",i);
break;
}
}
}
else{
puts("No");
printf("%d",f[T]);
}
return 0;
}
子矩阵咕咕咕
PJ5 字符串处理
乒乓球
#include<bits/stdc++.h>
using namespace std;
inline void work(string s,int score){
int a=0,b=0;
for(int i=0;i<s.size() && s[i]!='E';++i){
if(s[i]=='W')a++;
else b++;
if(max(a,b)>=score && abs(a-b)>=2){/////////////////////////////
printf("%d:%d
",a,b);
a=b=0;
}
}
printf("%d:%d
",a,b);
}
int main(){
string str,line;
while(cin>>line)str+=line;
work(str,11);
puts("");
work(str,21);
return 0;
}
ISBN 号码
#include<bits/stdc++.h>
using namespace std;
char ss[1<<21],*p1=ss,*p2=ss;
inline char gc(){return p1==p2 && (p2=(p1=ss)+fread(ss,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
template <typename T>inline void rd(T &x){x=0;char c=gc();int f=0;while(!isdigit(c)){f|=c=='-';c=gc();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}x=f?-x:x;}
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,u) for(int i=head[u];i;i=e[i].next)
int wei,ans;
int main(){
#ifdef WIN32
freopen("isbn.txt","r",stdin);
#endif
string s;
cin>>s;
for(int i=0;i+1<s.size();++i){
if(s[i]=='-')continue;
wei++;
if(s[i]=='X')ans+=wei*10;
else ans+=wei*(s[i]-'0');
}
ans%=11;
char t='X';
if(ans<10)t=ans+'0';
if(s.back()==t)puts("Right");
else {
s.back()=t;
cout<<s;
}
return 0;
}
多项式输出
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;cin>>n;
for(int i=n;i>=0;--i){
int a;scanf("%d",&a);
if(a==0)continue;
if(i<n && a>0)printf("+");//不是首项
else if(a<0)printf("-");
a=a<0?-a:a;
if(a>1 || i==0)printf("%d",a);//系数>1 || 是常数项 才输出
if(i>0)printf("x");//不是常数项都输出x
if(a>0 && i>1)printf("^%d",i);//不是末尾两项 && 系数不为0
}
return 0;
}
PJ DP3 10.22
传球游戏
算法:DP,环形DP
复杂度 O(N* M)
SOL:
不妨设小蛮在0号,所有人的编号是0∼n−1。
状态表示 f[i, j]
:
- 集合:第j次传球后球在第i号同学手里的方案数
- 属性:集合中元素的数量;
状态计算:
-
f[i, j]
所表示的集合可以划分成两个子集:
- 从左边传过来的集合大小是
f[i-1,j-1]
; - 从右边传过来的集合大小是
f[i+1,j+1]
;
- 从左边传过来的集合大小是
-
f[i,j]
等于两个子集的元素数之和。
注意当i==0
或j==n-1
时需要特殊处理边界(因为是环状的)。
最终答案就是f[0][m]
。
const int N=35;
int f[N][N];//第j次传球后球在第i号同学手里的方案数
int n,m;
int main(){
#ifdef WIN32
freopen("a.txt","r",stdin);
#endif
rd(n),rd(m);
f[0][0]=1;//设小蛮是第0号
rep(j,1,m)
rep(i,0,n-1)
f[i][j]=f[(i-1+n)%n][j-1]+f[(i+1)%n][j-1];
printf("%d",f[0][m]);
return 0;
}
摆花
算法知识点:DP,多重背包问题,背包问题求方案数
复杂度:O((n^{2}a))
问题转化:
- 将花盆数量看作背包容量;
- 将花看作物品,体积是1,第 i 种物品最多选 (a_i) 个;
- 问题:将背包装满的方案数是多少?
这是典型的多重背包求方案数问题:
- 状态表示:
f[i,j]
表示前i
个物品,总体积是j
的方案数; - 状态计算:
f[i,j]=f[i-1,j]+f[i-1,j-1]+...+f[i-1,j-a[i]]
。
const int N=110,mod=1000007;
int f[N],a[N];
int n,m;
int main(){
rd(n),rd(m);
rep(i,1,n)rd(a[i]);
f[0]=1;
rep(i,1,n){
dwn(j,m,0)
rep(k,1,min(a[i],j))
f[j]=(f[j]+f[j-k])%mod;
}
printf("%d
",f[m]);
return 0;
}