感谢怡巨提供的“1A”训练cf题,没做到几个1A,把代码错误点都再次总结一遍,省赛不能犯这种低级错误

会的1A,不会的瞎搞,这不就是acm的真谛吗?


629A

1A过的

题意:统计有多少个匹配,一个匹配意味着有两个C字母在同一行或者一列

暴力统计每行每列的C字母个数,然后一个求和公式就搞定

int row[maxn];
int col[maxn];
int n;
char mp[maxn][maxn];

int main(){
	//input;
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
		memset(row,0,sizeof(row));
		memset(col,0,sizeof(col));
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if (mp[i][j]=='C')
					row[i]++,col[j]++;
		int ans=0;
		for(int i=1;i<=n;i++) ans+=(row[i]-1)*row[i]/2;
		for(int i=1;i<=n;i++) ans+=(col[i]-1)*col[i]/2;
		printf("%d\n",ans);
	}
	return 0;
}

659A

题意:圆桌转圈,n个点,需要从起点走到终点,已知步数

1A过的,很简单,步数对点数取余之后,根据正负处理走向即可

int n,a,b;

int main(){
	//input;
	while(scanf("%d%d%d",&n,&a,&b)!=EOF){
		b=b%n;
		a+=b;
		while(a>=n) a-=n;
		while(a<=0) a+=n;
		cout<<a<<endl;
	}
	return 0;
}

659C

难点在看题之后的转换,要是把样例2的output写成1235肯定可以看出来怎么做了

方法是贪心:从1一直往后算,只要没有出现过并且买得起就买,记得用二分搜索判断是否在原数组中出现过,因此预处理需要排序

int n,m,ans,Left;
int num[maxn];
int ansnum[maxn];

bool canfind(int x){
	int l=1,r=n,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if (num[mid]==x) return true;
		else if (num[mid]>x) r=mid-1;
		else l=mid+1;
	}
	return false;
}

int main(){
	//input;
	while(scanf("%d",&n)!=EOF){
		scanf("%d",&m);
		for(int i=1;i<=n;i++) scanf("%d",&num[i]);
		sort(num+1,num+n+1);
		ans=0;
		Left=m;
		for(int i=1;i<=m;i++){
			if (Left<i) break;
			if (!canfind(i)){
				ans++;
				Left-=i;
				ansnum[ans]=i;
			}
		}
		printf("%d\n",ans);
		for(int i=1;i<=ans;i++)
			printf("%d%c",ansnum[i],i==ans?'\n':' ');
	}
	return 0;
} 

651A

2A过的是因为没有处理边界(题目的意思我没懂1 1这种特殊情况到底应该怎么算)

有很多方法做这个题,数据量最大100*100,暴力两重for应该也能过的

选用记忆化DP是因为代码好写,从输入的nm倒着来就好

int dp[maxn][maxn];

int getdp(int x,int y){
	if (dp[x][y]!=-1) return dp[x][y];
	if (x<=0||y<=0) return dp[x][y]=0;
	if (x==1) return dp[x][y]=getdp(x+1,y-2)+1;
	if (y==1) return dp[x][y]=getdp(x-2,y+1)+1;
	return dp[x][y]=max(getdp(x+1,y-2),getdp(x-2,y+1))+1; 
}

int main(){
	int n,m;
	memset(dp,-1,sizeof(dp));
	while(scanf("%d%d",&n,&m)!=EOF){
		if (n==1&&m==1) cout<<"0"<<endl;
		else printf("%d\n",getdp(n,m));
	}
	return 0;
} 

651C

做得特别差的一个题,5A

错因只有一个:不知道怎么判定边界,即现在处理的数据是不是在输入的数据集合

因为要for循环找符合条件的,如何给定限制,又符合题意条件又处理好了最后一个数的边界,换了好多种表示

题意是找到给定点集中,两点的定义的两个距离相等的数目对

即为:629A

只需要排序之后,统计在同一直线上的点的数目即可

还有一个自己避免了的wa点:__int64

struct node{
	int x,y;
}a[maxn];

int n;

int cmpx(node m,node n){
	if (m.x==n.x) return m.y<n.y;
	return m.x<n.x;
}

int cmpy(node m,node n){
	if (m.y==n.y) return m.x<n.x;
	return m.y<n.y;
}

int main(){
	//input;
	int i,j;
	__int64 ans,temp;
	while(scanf("%d",&n)!=EOF){
		for(i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
		ans=0;
		sort(a+1,a+n+1,cmpx);
		for(i=1;i<=n;){
			temp=0;
			for(j=i;j<=n;j++)
				if (a[j].x==a[i].x) temp++;
				else break;
			ans+=temp*(temp-1)/2;
			i=j;
		}
		sort(a+1,a+n+1,cmpy);
		for(i=1;i<=n;){
			temp=0;
			for(j=i;j<=n;j++)
				if (a[j].y==a[i].y) temp++;
				else break;
			ans+=temp*(temp-1)/2;
			i=j;
		}
		for(i=1;i<=n;){
			temp=0;
			for(j=i;j<=n;j++)
				if (a[j].x==a[i].x&&a[j].y==a[i].y) temp++;
				else break;
			ans-=temp*(temp-1)/2;
			i=j;
		}
		printf("%I64d\n",ans);
	}
	return 0;
}

629B

语文水平不够被这个题绕进去了

说要求男女人数相等,且求人数最多的日期

3A才过是因为:不是符合男女人数相等的日期才有意义,而是每个日期中,选择男女人数较少的那个,然后再匹配异性不就ok了吗?

表达可能还是比较绕,看了代码肯定明白:

struct node{
	char s[5];
	int x,y;
}a[maxn];

int n,man,woman,ans;

int main(){
	//input;
	while(scanf("%d",&n)!=EOF){
		ans=0;
		for(int i=1;i<=n;i++) scanf("%s%d%d",a[i].s,&a[i].x,&a[i].y);
		for(int i=1;i<=366;i++){
			man=woman=0;
			for(int j=1;j<=n;j++)
				if (a[j].x<=i&&a[j].y>=i)
					if (a[j].s[0]=='M') man++;
					else woman++;
			man=min(man,woman);
			ans=max(ans,man*2);
		}
		printf("%d\n",ans);
	}
	return 0;
} 

659B

一个蠢得不能再蠢的排序题都要2A

是个赶紧退役的理由了

题意中强调每个组别至少两个人:意味着该组只有两个人的时候肯定有解

否则一定要该组的最大值和次大值比第三大值严格大

wa的一发没有判断该组两个人的情况

struct node{
	string s;
	int pos;
	int score;
}a[maxn],ans1,ans2;

int cmp(node x,node y){
	if (x.pos==y.pos) return x.score>y.score;
	return x.pos<y.pos;
}

int main(){
	//input;
	int n,m,i,j,pos,flag,num;
	while(scanf("%d%d",&n,&m)!=EOF){
		for(i=1;i<=n;i++){
			cin>>a[i].s;
			scanf("%d%d",&a[i].pos,&a[i].score);
		}
		sort(a+1,a+n+1,cmp);
		pos=1;j=1;
		while(pos<=m){
			flag=false;
			i=j;
			j=i+1;
			while(a[i].pos==a[j].pos) j++;
			num=j-i;
			if (num==2){
				ans1=a[i];
				ans2=a[i+1];
				flag=true;
			}
			else if (a[i].score>a[i+2].score&&a[i+1].score>a[i+2].score
				&&a[i].pos==a[i+2].pos&&a[i+1].pos==a[i+2].pos){
				ans1=a[i];
				ans2=a[i+1];
				flag=true;
			}
			pos++;
			if (flag) cout<<ans1.s<<" "<<ans2.s<<endl;
			else cout<<"?"<<endl;
		}
	}
	return 0;
}

651B

排序加深搜(相当于并查集吧)

需要严格找当前比自己大的最小的数,那就直接排序从前往后找,用过的数进行标记即可

2A是因为在dfs中忘了对vis数组进行标记,蠢成。。。

小技巧是设置inf作为边界标记,到了之后就到头了

int n,a[maxn],ans;
bool vis[maxn];

void dfs(int pos){
	int i;
	for(i=pos+1;;i++)
		if (a[i]>a[pos]&&!vis[i]) break;
	if (a[i]==inf) return;
	else{
		vis[i]=true;
		ans++;
		dfs(i);
	}
	return;
}

int main(){
	//input;
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		a[n+1]=inf;
		sort(a+1,a+n+1);
		memset(vis,0,sizeof(vis));
		ans=0;
		for(int i=1;i<=n;i++)
			if (!vis[i]){
				vis[i]=true;
				dfs(i);
			}
		printf("%d\n",ans);
	}
	return 0;
}

做了这些题之后发现简单题的代码思想基本都在

暴力,搜索,简单dp

需要再上一层楼估计很难,也没有那么多的时间来学习新的算法

省赛需要做的就是把简单细节做好


还有两个坑等待填,暂时不会

http://codeforces.com/contest/629/problem/C


http://codeforces.com/contest/659/problem/D