高一高考集训总结赛
牛魔电脑,电源线这么弱不禁风😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠
A.乌龟棋
这题还是挺碰运气的. 赛时试了一个二维转移,开的位置和上次的选择. 然后发现这个煞笔方程没考虑限制情况.
然后急了,想写爆搜打表,写完了发现这题并没有什么可打表的. 还是老老实实写 DP,喷空十分钟时候写了这样一个逆天东西:
int f[351][41][41][41][41]
然后发现这东西虽然逆天,但是还挺好转移的,遂把第一维砍了(为什么当时我不把第五维砍了,好奇怪)
#include<bits/stdc++.h>
using namespace std;
int f[41][41][41][41];
int n,m;
int a,b,c,d,tot;
int w[351];
int main(){
#ifdef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=m;i++){
int x;cin>>x;
if(x==1)a++;
if(x==2)b++;
if(x==3)c++;
if(x==4)d++;
}
f[1][1][1][1]=w[1];
for(int i=1;i<=a+1;i++){
for(int j=1;j<=b+1;j++){
for(int k=1;k<=c+1;k++){
for(int l=1;l<=d+1;l++){
tot=i-1+(j-1)*2+(k-1)*3+(l-1)*4+1;
f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l]+w[tot]);
f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l]+w[tot]);
f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k-1][l]+w[tot]);
f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k][l-1]+w[tot]);
}
}
}
}
cout<<f[a+1][b+1][c+1][d+1]<<endl;
}
预估 \(100pts\)
实际 \(0pts\)
B.划分大理石
赛时看见这题差点似在座位上,以为又是昨天那种若只气球题.
但是我就真的以为这是个若只气球题,赛时打的也是很逆天
#include<bits/stdc++.h>
using namespace std;
int main(){
#ifdef ONLINE_JUDGE
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
#endif
while(1){int sum=0,x;for(int i=1;i<=6;++i)
cin>>x,sum+=x*i;if(!sum)break;cout<<((sum&1)?"Can't":"Can")<<endl;}
}
正确的
#include<bits/stdc++.h>
using namespace std;
int a[7],vis[20001];
bool f[10001];
int main(){
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
while(1){
a[0]=0;
for(int i=1;i<=6;++i){
cin>>a[i];
a[0]+=a[i]*i;
}
if(a[0]==0)break;
if(a[0]&1){
cout<<"Can't"<<endl;
continue;
}
memset(f,0,sizeof f);
a[0]/=2;
f[0]=1;
for(int i=1;i<=6;++i){
for(int j=0;j<=a[0];++j){
vis[j]=0;
}
for(int j=i;j<=a[0];++j){
if(!f[j]&&f[j-i]&&vis[j-i]<a[i]){
f[j]=1,vis[j]=vis[j-i]+1;
}
}
}
cout<<"Can"<<(f[a[0]]?"":"'t")<<endl;
}
}
预估 \(10pts\)
实际本来应该拿 \(100pts\)
但是拿了 \(0pts\)
C.干草堆
我要是说我赛时推了一种单调队列优化的贪心并查集图论做法...
实际上只是贪心就能拿 \(47pts\),我这个想的还要多一点,怎么也得有 \(50\) 分吧(心虚)
你们怎么想的都跟我差不多,我一说这个做法,你们都说想过类似的.
实际上要用 DP,但是我从头至尾没想过 DP 😥.
开一个线性的一位 DP,然后倒着求一个前缀和
求前缀和干嘛呢?考虑到如果层数最高,那么底层就一定需要放最少的长度,否则这个情况无法成为最优决策.
那我们用 \(f_{i}\) 表示 \([1,i]\) 这些块能搭多少层,我们在从 \(j\) 向 \(i\) 扩展的时候,肯定是往下面再加一层,那么新加的长度就不能低于已有的长度.
做到这里可以发现这个 \(f_{i}\) 不是我们想维护的 DP 数组,因为它太好维护了,关键是怎么找这个最优的 \(j\).
对此,我们为 \(j\) 开一个 \(minlen_{j}\),表示 \(j\) 放在最后一层的时候,最后一层的最小长度,这样我们找 \(j\) 的条件就变成 \(minlen(j)\ge sum_{i}-sum_{j-1}\).
找 \(j\),\(j\) 满足 \(j=max(j\mid minlen(j)\ge sum_{i}-sum_{j-1})\).
在考虑怎么转移 \(minlen_{j}\),\(minlen_{i}=sum_{i}-sum_{j-1}\),找到 \(j\) 之后就能向后转移了.
唯一没想到的是 \(n^{2}\) 能过,数据仍需努力.
\(minlen(j)\ge sum_{i}-sum_{j-1}\) 移项为 \(minlen(j)+sum_{j-1}\ge sum_{i}\)
对 \(minlen(j)+sum_{j-1}\) 维护一个单调队列即可.
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001],s[100001],f[100001],minlen[100001];
int q[100001];
int main(){
#ifdef ONLINE_JUDGE
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=1;i<=n;++i){
s[i]=s[i-1]+a[n-i+1];
}
int l=1,r=0;
for(int i=1;i<=n;++i){
while(l<=r&&s[q[l]]+minlen[q[l]]<=s[i]) l++;
f[i]=f[q[l-1]]+1;
minlen[i]=s[i]-s[q[l-1]];
while(l<=r&&s[q[r]]+minlen[q[r]]>=s[i]+minlen[i]) r--;
q[++r]=i;
}
cout<<f[n]<<endl;
}
D.围栏障碍训练场
赛时我是有想法的,电脑被爆破之后一直在做这个题
看看我的赛时代码
#include<bits/stdc++.h>
using namespace std;
int n,s;int from[30001][2][2];int f[30001][2];
inline int dis(int x,int y){
return abs(x-y);
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
cin>>n>>s;
for(int i=1;i<=n;++i){
cin>>from[i][0][0]>>from[i][1][0];
from[i][0][1]=from[i][0][0];
from[i][1][1]=from[i][1][0];
}
memset(f,0x3f,sizeof f);
if(from[n][0][0]<=s&&s<=from[n][1][0]){
f[n][0]=abs(s-from[n][0][0]);
f[n][1]=abs(s-from[n][1][0]);
}
else{
f[n][0]=f[n][1]=0;
from[n][0][1]=s;from[n][1][1]=s;
}
// cout<<n<<" "<<f[n][0]<<" "<<f[n][1]<<endl;
for(int i=n-1;i>=1;--i){
int li=from[i][0][1],ri=from[i][1][1];
// cout<<"left "<<from[i][0][1]<<" "<<from[i+1][0][1]<<endl;
if(li>from[i+1][0][1]){
// cout<<"jleft1 "<<i<<" "<<f[i+1][0]<<endl;
if(f[i+1][0]<f[i][0]){
from[i][0][1]=from[i+1][0][1];
f[i][0]=f[i+1][0];
}
}
if(li>from[i+1][1][1]){
// cout<<"jleft2 "<<i<<" "<<f[i+1][0]<<endl;
if(f[i+1][1]<f[i][0]){
from[i][0][1]=from[i+1][1][1];
f[i][0]=f[i+1][1];
}
}
// cout<<"right "<<from[i][1][1]<<" "<<from[i+1][1][1]<<endl;
if(ri<from[i+1][1][1]){
// cout<<"jright1 "<<i<<" "<<f[i+1][1]<<endl;
if(f[i+1][1]<f[i][1]){
from[i][1][1]=from[i+1][1][1];
f[i][1]=f[i+1][1];
}
}
if(ri<from[i+1][0][1]){
// cout<<"jright2 "<<i<<" "<<f[i+1][1]<<endl;
if(f[i+1][0]<f[i][1]){
from[i][1][1]=from[i+1][0][1];
f[i][1]=f[i+1][0];
}
}
// cout<<"dis "<<i<<" "<<from[i+1][1][0]<<" "<<from[i][1][0]<<" "<<dis(from[i+1][1][0],from[i][1][0])<<endl;
f[i][0]=min({
f[i][0],
f[i+1][0]+dis(from[i+1][0][1],from[i][0][0]),
f[i+1][1]+dis(from[i+1][1][1],from[i][0][0]),
f[i+1][0]+dis(from[i+1][0][0],from[i][0][0]),
f[i+1][1]+dis(from[i+1][1][0],from[i][0][0])
});
f[i][1]=min({
f[i][1],
f[i+1][0]+dis(from[i+1][0][1],from[i][1][0]),
f[i+1][1]+dis(from[i+1][1][1],from[i][1][0]),
f[i+1][0]+dis(from[i+1][0][0],from[i][1][0]),
f[i+1][1]+dis(from[i+1][1][0],from[i][1][0])
});
// cout<<i<<" "<<f[i][0]<<" "<<f[i][1]<<endl;
}
int ans=min(f[1][0]+abs(from[1][0][1]),f[1][1]+abs(from[1][1][1]));
cout<<ans;
}
/*
5 0
-8 -1
1 8
-8 -1
1 8
-8 -1
4 0
-2 1
-1 2
-3 0
-2 1
4 --.-
3----
2 ----
1 ----
3210123
*/
但是这题居然是线段树优化 DP,恼了.
没改完,别急