2020.03.18【NOIP提高组】模拟B 组17反思

这次比赛,不难,很水(即使没人AK)。Rank.1,370分。
还没开始提交,我就已经把全部题都做完了,但是考虑的不够周全,丢了一些分。
估分:100+100+100+100=400
实际:100+100+90+80
T1
平台
Description
Alice要搭建平台,平台不能漂在空气中,必须要有两根柱子支撑,具体地说,每个平台的两端必须由一根柱子支撑,柱子的另一端在地板或另一个平台上。
  给你平台的放置位置(如下左图所示),每个平台的位置由它的高度(离地面的垂直距离)和水平方向两个端点的坐标决定,每根柱子必须安放在离端点0.5个单位的位置。
  编程计算所需柱子总长是多少。
Solution
直接暴力即可。O(n^2)
估分:100
实际:100

#include<cstdio>
using namespace std;
int n,y[101],x[101],x2[101],ans;
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d",&y[i],&x[i],&x2[i]);
	for(int i=1;i<=n;i++) {
		int maxn=0,MAX=0;
		for(int j=1;j<=n;j++)
			if(y[j]<y[i]) {
				if(x[j]<=x[i]&&x2[j]>x[i]&&y[j]>maxn)
					maxn=y[j];
				if(x[j]<x2[i]&&x2[j]>=x2[i]&&y[j]>MAX)
					MAX=y[j];
			}
		ans+=y[i]-maxn+y[i]-MAX;
	}
	printf("%d",ans);
}

T2
单足跳
Description
游戏在一行N个方块中进行,编号为1到N,一开始Alice在方块1中,第一次只能跳到方块2中,接下来每一次跳跃必须满足以下两个限制:
  (1) 如果是向前跳(即跳到比现在编号大的方块),跳跃距离必须比上一次要大1;
  (2) 如果是向后跳(即跳到比现在编号小的方块),跳跃距离必须跟上一次一样。
  例如,第一次跳跃后,Alice可以跳回1也可以跳到4。
  每进入一个方块,Alice必须支付一定的费用,Alice的目标花最少的钱从方块1跳到方块N。编程计算最小的花费。
Solution
dp。设f[i][j]表示跳i的距离,到j这个格子的最小分数。
很容易得到方程:

f[i][j]=min(f[i][j],f[i-1][j-i]+a[j]);
f[i][j]=min(f[i][j],f[i][i+j]+a[j]);

估分:100
实际:100

#include<cstdio>
#include<cstring>
using namespace std;
int n,a[1001],f[1001][1001],ans=0x3f3f3f3f;
int min(int x,int y) {
	if(x<y)return x;
	return y;
} 
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	memset(f,0x3f3f3f3f,sizeof(f));
	f[0][1]=0;
	for(int i=1;i<=n;i++)
		for(int j=n;j;j--) {//注意j要倒着 
			if(j+i<=n)f[i][j]=min(f[i][j],f[i][j+i]+a[j]);
			if(j-i>0)f[i][j]=min(f[i][j],f[i-1][j-i]+a[j]);
			if(j==n)ans=min(ans,f[i][j]);
		}
	printf("%d",ans);
}

T3
生日聚餐
Description
Alice在餐馆里当服务员,今天是她生日,她请求厨师帮她准备生日晚餐,晚餐由N种原料做成,每道菜所需每种原料的数量是一样的。
  厨房里有一些原料,但不够,Alice还需要从旁边的超市中购买一些回来。超市里什么原料都有,每种原料都分大包装和小包装。Alice有M元钱,她想利用这M元钱购买原料使得能做出最多的菜。
Solution
先考虑dp,设f[i][j]表示第i种原料用了j块钱的最大数量。转移就用类似背包的东西,不在赘述。
然后,考虑用二分。判断一个mid是否正确,枚举每一个原料,然后二分查找f[i][j]等于mid的一个最小j。
最后把所有j相加,判断是否超出m,如果超出m,则是不合法。
估分:100
实际:90(比赛时是另一种二分,空间卡住了)

#include<cstdio>
using namespace std;
int n,m,a[101],b[101],f[101][100001],l=0,r=100000,ans;
int max(int c,int d) {
	if(c>d)return c;
	return d;
}
inline bool check(int k) {
	int t=0;
	for(register int i=1;i<=n;i++) {
		if(f[i][m]<k)return 0;
		int left=0,right=m,ret=0;
		while(left<=right) {
			int middle=(left+right)>>1;
			if(f[i][middle]>=k)right=middle-1,ret=middle;
			else left=middle+1;
		}
		t+=ret;
	}
	if(t<=m)return 1;
	return 0;
}
int read() {
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')
		s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s;
}
int main() {
	n=read();m=read();
	for(register int i=1;i<=n;i++) {
		a[i]=read();
		b[i]=read();
		int x,y,u,v;
		x=read();y=read();
		u=read();v=read();
		for(register int j=1;j<=m;j++) {
			if(j-y>=0)f[i][j]=max(f[i][j],f[i][j-y]+x);
			if(j-v>=0)f[i][j]=max(f[i][j],f[i][j-v]+u);
		}
		for(register int j=1;j<=m;j++)
			f[i][j]=(f[i][j]+b[i])/a[i];
	}
	while(l<=r) {
		int mid=(l+r)>>1;
		if(check(mid))l=mid+1,ans=mid;
		else r=mid-1;
	}
	printf("%d",ans);
} 

T4
数学题
Description
当Alice在浏览数学书时,看到一个等式A=S,奇怪的是A和S并不相等。Alice发现可以通过在A中添加加号“+”从而使得等式成立。
  编程计算最少需要插入多少加号使得等式成立。允许每个数有多个前导0。
Solution
So easy。但是有两个点把我给卡了。也是考虑dp(dp题真多)。
设f[i][j]表示到第i位和为j的最小加号数。
转移很显然,不赘述。
估分:100
实际:80(被卡了两个点,没有过滤掉0)

#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int s=0,n,a[1001],f[1001][5001];
inline void init() {
	int len=0;
	char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9')a[++len]=c^48,c=getchar();
	n=len;
	c=getchar();
	while(c>='0'&&c<='9')s=(s<<1)+(s<<3)+(c^48),c=getchar();
	for(int i=0;i<=n;i++)
		for(int j=0;j<=s;j++)
			f[i][j]=1005;
}
inline int min(int u,int v) {
	if(u<v)return u;
	return v;
}
inline void solve() {
	f[0][0]=0;
	for(register int i=0; i<n; i++) {
		int l=i+1;
		while(a[l]==0&&l<=n)++l;//一定要把开头0过滤掉,否则有两个点跑步过去 
		if(l>n)l=n;
		for(int j=0; j<=s; j++) {
			if(f[i][j]==1005)continue;
			int x=0;
			for(register int k=l; k<=n; k++) {
				x=(x<<1)+(x<<3)+a[k];
				if(x+j>s)break;
				f[k][j+x]=min(f[k][j+x],f[i][j]+(k!=n));
			}
		}
	}
	printf("%d",f[n][s]);
}
signed int main() {
	init();
	solve();
}

额……NOI Online连暴力都码错了,一共只有10分,作为初一的蒟蒻,还要努力。
反思:
1、一定要考虑的周全一点(包括时间,空间,数据大小等……)。
下次加油!

猜你喜欢

转载自blog.csdn.net/MZHjr/article/details/104967089
今日推荐