NOIP模拟赛2019.8.30题解

T1 最大序列

100pts

维护一个
从前往后扫描,如果栈为空或当前数及之后数中的最大值大于栈顶的数,就将最大值之前的所有数入栈,并且把最大值保存在答案中。
否则将栈顶的元素弹出,并保存在答案中。
由于最大值从后往前是单调递增的,所以直接用一个数组维护就可以O(1)查询

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,a[N],ans[N],f[N],tot,now;
stack<int>s;
int read()
{
	int sum=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 
	while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();} 
	return sum*f;
}
void print(int x)
{
	if(x<0)x=-x,putchar('-');
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
int main()
{
//	freopen("seq.in","r",stdin);
//	freopen("seq.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
	a[i]=read();
	for(int i=n;i>=1;i--)
	{f[i]=max(f[i+1],a[i]);}
    int i=1;
	while(i<=n+1)	
	{
	  if(!s.size()||f[i]>s.top())
	  { 
	    int now=f[i];
	  	while(a[i]!=now){s.push(a[i]);i++;}
	    ans[++tot]=now;
	    i++;
	  }
	  else{
	  	ans[++tot]=s.top();
	  	s.pop();
	  }
	}
	for(int i=1;i<=n;i++)
	{
		print(ans[i]);
		if(i!=n)putchar(' ');
	}
    return 0;	
} 

T2 斩杀计划

100pts

用一个桶 b [ ] b[ ] b[]统计所有攻击力的小弟个数。
从小到大枚举使用腐败药水的次数,把能拉过来的小弟都拉过来,当小弟的攻击力ans>=sum (血量总值)时就跳出。这样保证腐败药水的次数一定是最小的。
保证ans>=sum的前提下,删去小弟中多余的。
先删3 因为3的花费为4,显然最不划算
然后删2 因为2和1的花费相同,但2比1攻击力贡献多1
最后删1

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=5000005,maxn=30000;
int n,m,sum,x,ans,s1,s2,s3,tot;
int b[N];
int read()
{
	int sum=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 
	while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();} 
	return sum*f;
}
void print(int x)
{
	if(x<0)x=-x,putchar('-');
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
bool comp(int a,int b){
	return a>b;
}
int main()
{
//	freopen("zhanshajihua.in","r",stdin);
//	freopen("zhanshajihua.out","w",stdout);
	n=read();sum=read();
    for(int i=1;i<=n;i++)
    {x=read();b[x]++;}
    for(int i=1;i+2<=maxn;i+=3)
    {
    	s1+=b[i];s2+=b[i+1];s3+=b[i+2];
		ans=s1+s2*2+s3*3;
		if(ans>=sum)break;
		tot++;	 
	}
	if(ans<sum){printf("-1");return 0;}
	while(ans-3>=sum&&s3){s3--;ans-=3;}
	while(ans-1>=sum&&s1){s1--;ans-=1;}
	while(ans-2>=sum&&s2){s2--;ans-=2;}
    print(tot);putchar(' ');
	print(s1+s2+s3*4+tot);
    return 0;	
} 

T3 分离计划

100pts

先求出所有数的最大值ma,最小值mi。
然后二分枚举答案ans。
check()函数中把图分成A,B两部分:
若能保证A部分的所有数x,满足x>=ma-ans ;B部分的所有数y,满足y>=ans-mi,则当前ans合法。
否则把图旋转九十度,继续check,一直旋转到原来仍不合法,则当前ans不合法。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=2005,inf=0x3f3f3f3f;
int n,m,a[4][N][N],f[N][N],k=1,mi,ma; 
int max(int x,int y){ return x>y?x:y;} 
int min(int x,int y){ return x>y?y:x;} 
int read()
{
	int sum=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 
	while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();} 
	return sum*f;
}
void print(int x)
{
	if(x<0)x=-x,putchar('-');
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
void init(int x,int y)
{
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	a[y][j][n-i+1]=a[x][i][j];
	swap(n,m);
}
int judge(int now)
{
	int y=m;
	memset(f,0,sizeof(f));
	for(int i=1;i<=n;i++)
	for(int j=1;j<=y;j++)
	{
		if(ma-a[k][i][j]>now){y=j-1;break;}
		else f[i][j]=1;
	}
	for(int i=n;i>=1;i--)
	for(int j=m;j>=1;j--)
	{
		if(f[i][j])break;
		if(a[k][i][j]-mi>now)return 0;
	}
	return 1;
}
void change()
{
	k=(k+1)%4;swap(n,m);
}
int check(int now)
{
	if(judge(now))return 1;change(); 
	if(judge(now))return 1;change(); 
	if(judge(now))return 1;change(); 
	if(judge(now))return 1;change(); 
	return 0;
}
int main()
{
//	freopen("separate.in","r",stdin);
//	freopen("separate.out","w",stdout);
	n=read();m=read();
	mi=inf;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
    {
	a[0][i][j]=read(); 
	ma=max(ma,a[0][i][j]);
	mi=min(mi,a[0][i][j]);
	}
    init(0,1);init(1,2);init(2,3);
	int l=0,r=ma-mi;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	print(l);
    return 0;	
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值