jzoj_1月29日D组

第一题(noip)

题意:

    给出一段长度为n的字符串,里面只有NOI三个字母组成,我们现在要求插入一个字符,这个字符可以等于NOI中的其中一个字符,求我们插入这个字符后我们可以有多少种选法选成NOI这一串。

思路:

    我们可以求出原来没插入字符前选NOI这一串的选法,它就等于每个O的前面N的个数*每个O的后面的I的个数,例如,NOIOI中,它原来的选法等于N OIOI:1*2,NOI OI:1*1,我们把他们加起来就等于结果3,现在我们来一个一个枚举N和I的位置,N放进的最优值是每个O后I的数量的和,I放进的最优值是每个O前N的数量的和,之后还有O的位置,O的位置是max(res_O,a[i]*b[i]),a[i]表示i前面N的个数,b[i]表示i后面I的个数,之后我们求出放n,o,i的最优值的最大值再加上原来的选法就是答案。

代码:

#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int n,res_N,res_O,res_I,a[100001],b[100001],t,ti,o[100001],ks;
unsigned long long ans;
char c[100001];
unsigned long long max(int x,int y){return x>y?x:y;}
int main()
{
	freopen("noip.in","r",stdin);
	freopen("noip.out","w",stdout);
	scanf("%d\n",&n);
	gets(c);
	for (int i=0;i<n;i++)
	  if (c[i]=='I') b[0]++;
	for (int i=0;i<n;i++)
	{
		a[i]=a[i-1];
		if(i)b[i]=b[i-1];
		if (c[i]=='N') a[i]++;//求出i前位置N的个数
		if (c[i]=='I') b[i]--;//求出i后位置I的个数
		if (c[i]=='O'){ans+=a[i]*b[i];res_N+=b[i];res_I+=a[i];}//res_N和res_I分别代表放入N和I的最优值                                                                                                                     
	}                                                              //ans是原来的选法 
       for (int i=0;i<n;i++)
	  res_O=max(res_O,a[i]*b[i]);求出放入O的最优值
	printf("%lld",ans+max(res_N,max(res_O,res_I)));//加上插入字符后的值
}

第二题(小麦高度)

题意:

    有两个人玩游戏,第一个人会选择n个小麦中最短的小麦把它延长到第二短的小麦,第二个人会选择最长的小麦把它剪短到第二长的小麦,先让第一个人开始游戏,当小麦的高度不足三种时,正在进行游戏的人就输了。

思路:

    因为要求有几种高度,所以我们可以用桶来统计,设置两个东西来指向最高的桶和最低的桶,之后进行游戏,最后只剩两个桶的时候比较高桶的数量和低桶的数量,如果低桶数量小于高桶数量就是第二个人赢,否则就是第一个人赢,当然,如果游戏没开始前就只有两种高度我们就输出第二个人赢。

代码:

#include<cstdio>
int n,b[100001],x,high,low,m;
struct node{
	int cnt,val;
}q[100001];
int min(int x,int y){return x<y?x:y;}
int main()
{
	freopen("wheat.in","r",stdin);
	freopen("wheat.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x); b[x]++;//b表示桶
	}
	low=1;//low是最小的桶的下标
	for (int i=1;i<=100000;i++)
	 if (b[i]) q[++high].cnt=b[i],q[high].val=i;//cnt存数量,val存数值
	if (high-low+1<3) {printf("Sarah\n%d %d",q[low].val,q[high].val);return 0;}//判断一开始有几种高度
	while (high-low+1>=3)
	{
		int kj=min(q[low].cnt,q[high].cnt);//快速进行游戏
		q[low].cnt-=kj;q[high].cnt-=kj;
		q[low+1].cnt+=kj;q[high-1].cnt+=kj;
		if (q[low].cnt==0) low++;//如果最小的桶为0了我们就让下标加
		if (q[high].cnt==0) high--;//如果最大的桶为0了我们就让下标减
	}
	if (q[low].cnt<=q[high].cnt) printf("Sarah\n%d %d",q[low].val,q[high].val);
	else printf("Smart\n%d %d",q[low].val,q[high].val);//判断输出
}

第三题(铺设地板)

题意:

    在一个矩阵里填字母,这些字母相同的连起来必须是正方形,1个字母也是个正方形,我们还要把这些字母从左到右,从上到下合起来,变成一个字符串,还要让这个字符串变得最小。

思路:

    我们可以用贪心,可以从每个没有填过的地板来枚举那一块能填的最小字母,如果可以扩展成一个正方形,我们就扩展,但要注意我们能不能填那里。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
char c[101][101];
int ask(int x,int y)//这个是来判断当前能填的最小字母的
{
	for (int i=0;i<=26;i++)
	if (c[x][y+1]!='A'+i&&c[x-1][y]!='A'+i&&c[x][y-1]!='A'+i)//我们填的字母不能和相连的地板相同
	return i;
}
bool ok(int x,int y,int l,int t)//判断此处能不能填边长为l最小字母为t的地板
{
	return (!c[x][y+l]&&!c[x+l][y]&&t+'A'!=c[x+l][y-1]&&t+'A'!=c[x-1][y+l]&&t<=ask(x,y+l));//我们要填的话,就不
}                                                              //能和即将扩展的正方形的上左右边的字母相同
void fill(int x,int y)
{
	int t=ask(x,y),l;
	c[x][y]=t+'A';//t为最小字母
	for (l=1;l<=min(n-x,m-y)&&ok(x,y,l,t);l++)//求出能扩展正方形的边长
	c[x][y+l]=t+'A';
	for (int i=x;i<=x+l-1;i++)
	  for (int j=y;j<=y+l-1;j++)
	    c[i][j]=t+'A';//扩展边长为l的正方形
}
int main()
{
	freopen("floor.in","r",stdin);
	freopen("floor.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	  for (int j=1;j<=m;j++)
	    if (!c[i][j]) fill(i,j);//如果没有填我们就去填
	for (int i=1;i<=n;i++)
	{
	    for (int j=1;j<=m;j++)
	      printf("%c",c[i][j]);	
		printf("\n");	
	}
}

第四题(邮票)

题意:

    给出s种邮票,每封信最多贴e张邮票,我们要求能贴出多少种面值的邮票的最大连续集。

思路:

    用f[i]代表贴出i最少需要的邮票张数,如果求出了f[j],则f[j+d[i]]=min(f[j]+1,f[j+d[i]]),其中d[i]表示s种邮票中其中一种的面值,初始化f[0]=0,之后就是统计最大连续集。

代码:

    
#include<cstdio>
#include<cstring>
int s,e,f[100001],d[21],m,tj,ans;
int min(int x,int y) {return x<y?x:y;}
int main()
{
	freopen("stamp.in","r",stdin);
	freopen("stamp.out","w",stdout);
	memset(f,127/3,sizeof(f));
	f[0]=0;
	scanf("%d%d",&s,&e);
	
	for (int i=1;i<=s;i++)
	scanf("%d",&d[i]),m=d[i]>m?d[i]:m;
	m*=e;
	for (int i=1;i<=s;i++)
	{
		for (int j=0;j<=m;j++)
		  f[j+d[i]]=min(f[j]+1,f[j+d[i]]);//求出每种面值要贴的最少张数  
        }
	for (int i=1;i<=m*e;i++)//因为最多只能贴出面值为m*e的邮票,所以只用
	{
		if (f[i]>e) f[i]=0;//如果要贴的张数大于e我们就让它等于0
		if (f[i]!=0) tj++;//不等于0才统计最大连续集
		else tj=0;ans=ans>tj?ans:tj;//如果等于0就让统计的tj清0,ans求最大值
	}
	printf("%d",ans);
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值