codeforces #356 Div2

Div2的A和B,是680A和680B

Div2的CDE,是679的A,B和C


给链接:680A

这次比赛,其实算是比较简单的,比赛的时候40分钟搞出来ABC,赛后补题可以补完D和E,div1还是确实不会

需要练1A的能力


A:给5个数字,均不超过100。可以删去两个相同的或者三个相同的,使得和最小

当时wa了一发,就是没有想明白两个和三个的坑点在哪

举两个数据:3 3 3 4 4,这个应该是删去3 3 3,得到的最小和是4+4=8

3 3 3 5 5,这个是删去5 5,得到的最小和是3+3+3=9

所以只需要把数据hash一下,然后判断,是删去三个还是两个的

代码:

int main(){
	//input;
	while(scanf("%d%d%d%d%d",&t1,&t2,&t3,&t4,&t5)!=EOF){
		memset(num,0,sizeof(num));
		ans=t1+t2+t3+t4+t5;
		num[t1]++;num[t2]++;num[t3]++;num[t4]++;num[t5]++;
		for(i=100;i>=1;i--)
			if (num[i]>=3) break;
		for(j=100;j>=1;j--)
			if (num[j]==2) break;
		if (i==0&&j==0) cout<<ans<<endl;
		else if (i>0&&j==0) cout<<ans-i*3<<endl;
		else if (i==0&&j>0) cout<<ans-j*2<<endl;
		else cout<<min(ans-i*3,ans-j*2)<<endl;
	}
	return 0;
}


B:从1到n个节点,你在a这个点上,从x到y的距离是abs(x-y)

t1,t2,t3……,tn表示当前格子是否有罪犯

根据题意,如果你的位置和当前格子的距离是有罪犯的,BCD这个工具就会有反应

所以,只需要枚举距离值就好

细节:要判断位置+距离是不是越界,位置-距离是不是出界

int main(){
	//input;
	while(scanf("%d%d",&n,&a)!=EOF){
		for(int i=1;i<=n;i++) scanf("%d",&num[i]);
		if (num[a]==1) ans=1;
		else ans=0;
		for(int i=1;i<=n;i++)
			if (a-i>=1&&a+i<=n&&num[a-i]==1&&num[a+i]==1) ans+=2;
			else if (a-i>=1&&a+i>n&&num[a-i]==1) ans++;
			else if (a-i<1&&a+i<=n&&num[a+i]==1) ans++;
		cout<<ans<<endl;
	}
	return 0;
}

C:典型脑洞题的

题意描述得很困难的样子:电脑随机生成a一个区间在【2,100】内,你猜一个数

你输入x,如果x是a的约数,那么返回yes,否则返回no

猜每个数的时候,最多询问20次,问你是否给出判断方案


这种题,20次,首先想到的是,【2,100】内有多少素数

百度了一下:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97这么多

数一下,不止20个啊,怎么办?有的是没有用的哎,不小于53的素数是不用尝试的

所以:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47共15个依次询问一遍,就可以得到是不是了

这样搞,wa了一发,想想为什么?平方数。素数的平方数会影响结论,即4,9,25,36

对这19个数问一遍,统计yes的个数,不超过1,说明是素数,否则是合数

int p[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,4,9,25,49};
string str[50];
int flag;
int ans;
int main(){
	flag=0;
	for(int i=0;i<19;i++){
		//fflush(p[i]);
		cout<<p[i]<<endl;
		cin>>str[i];
		if (str[i]=="yes") flag++;
	}
	if (flag<=1) cout<<"prime"<<endl;
	else cout<<"composite"<<endl;
	return 0;
}


D:贴个地址就好

http://www.wonter.net/?p=872

自己一开始想的是,删除得越多越好,想用贪心搞。但是不知道标准

其实这个题不要看m最多是1e15,其实立方数就1e6个,然后因为证明的数学规律给剪枝了很大一部分,每一步骤的选择,其实最多就是两种方案,所以,可以直接像贴的题解一样暴力搞好

代码就不贴了,链接中有


E:在n*n的地图中,允许自己添加一个k*k的联通块,求最大的联通的点的数量

这个题,第一想法我是搞对了的,先dfs,把图中的所有的联通块用1,2,3,4,……,x来标记出来,可以知道每一块多大,在哪

之后的呢,就是学习到了其他大神的题解

把k*k的矩阵,平移,在其与外面相连的4条边来统计,即:k*k+外面联通了多少

平移的时候,涉及到重复的问题,设计到k*k的矩阵中原有的数联通块的问题

细节分析见代码

#include<bits/stdc++.h>
using namespace std;

const int maxn=550;

char mp[maxn][maxn];
int cc[maxn][maxn];
int ccsize[maxn*maxn];
int added[maxn*maxn];
int dx[]={0,0,0,1,-1};
int dy[]={0,1,-1,0,0};
int n,k;

bool ok(int x,int y){
	if (x>=1&&x<=n&&y>=1&&y<=n) return true;
	return false;
}

void dfs(int x,int y,int num){
	cc[x][y]=num;
	++ccsize[num];
	for(int i=1;i<=4;i++){
		int nx=x+dx[i];
		int ny=y+dy[i];
		if (ok(nx,ny)&&mp[nx][ny]=='.'&&cc[nx][ny]==0) dfs(nx,ny,num);
	}
}

void add(int x,int y,int &ans,int num){
	//如果k*k外面的四条边中有联通块
	//而且之前没有加过,那么需要算进来
	if (ok(x,y)&&mp[x][y]=='.'){
		int id=cc[x][y];
		if (added[id]!=num){
			added[id]=num;
			ans+=ccsize[id];
		}
	}
}

int main(){
	//freopen("input.txt","r",stdin);
	int i,j,x,y,xlow,ylow;
	while(scanf("%d%d",&n,&k)!=EOF){
		for(i=1;i<=n;i++) scanf("%s",mp[i]+1);
		memset(cc,0,sizeof(cc));
		memset(ccsize,0,sizeof(ccsize));
		memset(added,0,sizeof(added));
		int cnt=0;
		//最简单的深搜
		//把所有的联通块处理好
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				if (mp[i][j]=='.'&&cc[i][j]==0) 
					dfs(i,j,++cnt);
		int id=1;
		int ans=0;
		//开始移动k*k的小矩阵
		for(ylow=1;ylow+k-1<=n;ylow++){
			//如果k*k里有数,是联通块的一部分
			//那么需要先减掉,以免算重复
			for(x=1;x<=k;x++)
				for(y=ylow;y<ylow+k;y++)
					--ccsize[cc[x][y]];
			for(xlow=1;xlow+k-1<=n;xlow++){
				int tmp=k*k;
				for(x=xlow;x<xlow+k;x++){
					add(x,ylow-1,tmp,id);//左边的一列
					add(x,ylow+k,tmp,id);//右边的一列
				}
				for(y=ylow;y<ylow+k;y++){
					add(xlow-1,y,tmp,id);//上边的一行
					add(xlow+k,y,tmp,id);//下边的一行
				}
				++id;
				ans=max(ans,tmp);
				if (xlow+k<=n)
					for(y=ylow;y<ylow+k;y++){
						//这次算完之后,要平移了
						//就要把左边的一列的值补回去
						//右边的值先删去联通块内的部分
						++ccsize[cc[xlow][y]];
						--ccsize[cc[xlow+k][y]];
					}
			}
			//这次的k*k全都移动完毕之后
			//要把值都恢复回来
			for(x=n-k+1;x<=n;x++)
				for(y=ylow;y<ylow+k;y++)
					++ccsize[cc[x][y]];
		}
		cout<<ans<<endl;
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值