高一高考集训总结赛

牛魔电脑,电源线这么弱不禁风😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠😠

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,恼了.

没改完,别急

posted @ 2024-06-13 15:09  HaneDaniko  阅读(59)  评论(2编辑  收藏  举报