第二十次CCF计算机软件能力认证 (CCF CSP 202009) 题解

第一题

题目大意

给出一个参考点,再给出 n n n个点,求这 n n n个点中距参考点欧几里得最近的 3 3 3个点的编号。如果距离相同,则按照编号升序排序。

解法

直接求出每个点距离参考点的距离,再排序即可。

如果不会排序,则扫三遍距离,每次找不同并且最小的即可。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2005;
int x,n,y;
struct node
{
	int id;
	double dis;
} pos[maxn];
double get(double x,double y,double xx,double yy)
{
	return (x-xx)*(x-xx)+(y-yy)*(y-yy);
}
bool cmp(node a,node b) {return a.dis==b.dis?a.id<b.id:a.dis<b.dis;}
int main()
{
	scanf("%d%d%d",&n,&x,&y);
	for(int i=1,tx,ty;i<=n;i++)
	{
		scanf("%d%d",&tx,&ty);
		pos[i].id=i;
		pos[i].dis=get(tx,ty,x,y);
	}
	sort(pos+1,pos+n,cmp);
	for(int i=1;i<=3;i++) printf("%d\n",pos[i].id);
	return 0;
}

第二题

题目大意

给出一个疫区,再给出 n n n个人各 t t t个时刻的行踪。如果某人某时刻在疫区内,则算经过;如果某人连续 t t t时刻都在疫区内,则算停留。求这 n n n个人中经过和停留疫区的各多少人。

解法

直接模拟即可,注意要连续 t t t时刻才算停留。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

int n,k,t,xl,yd,xr,yu,x,y;
bool check()
{
	if(x>=xl&&x<=xr&&y>=yd&&y<=yu) return true;
	return false;
}
int main()
{
	scanf("%d%d%d%d%d%d%d",&n,&k,&t,&xl,&yd,&xr,&yu);
	int ans1=0,ans2=0;
	for(int i=1;i<=n;i++)
	{
		int temp=0,t2=0;
		bool flag=false;
		for(int j=1;j<=t;j++)
		{
			scanf("%d%d",&x,&y);
			if(check())
			{
				temp++;
				t2++;
				if(t2>=k) flag=true;
			}
			else t2=0;
		}
		if(temp) ans1++;
		if(flag) ans2++;
	}
	printf("%d\n%d",ans1,ans2);
	return 0;
}

第三题

题目大意

给出许多逻辑器件,告诉每一个逻辑器件的输入,再给出外输入,求给定器件最后的输出。只考虑时序电路,即如果产生了循环,则输出"LOOP"。

题解

一眼看过去就是拓扑排序,先跑一遍求出拓扑序,然后就可以直接根据拓扑序进行处理。正好拓扑排序也可以判环,跑完拓扑排序后看是否还有点有入度,如果有,即有环。

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<string>
#include<vector>
using namespace std;
const int maxn=10005;
map<string,int> mp;
int T,type[maxn],in[maxn],que[maxn],head[maxn];
int sta[maxn],cnt=0,s,n,m,degree[maxn];
struct node
{
	int to,next;
} edge[maxn<<1];
inline void add(int u,int v)
{
	edge[++cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt;
}
void clear()
{
	memset(type,0,sizeof(type));
	memset(head,0,sizeof(head));
	memset(degree,0,sizeof(degree));
	memset(sta,0,sizeof(sta));
	cnt=0;
}
inline int getnum(string str)
{
	int x=0,len=str.length()-1;
	for(int i=1;i<=len;i++) x=(x<<1)+(x<<3)+str[i]-48;
	return x;
}
int main()
{
	#ifdef lemon
	freopen("C.txt","r",stdin);
	#endif
	mp["NOT"]=1;mp["AND"]=2;mp["OR"]=3;
	mp["XOR"]=4;mp["NAND"]=5;mp["NOR"]=6;
	scanf("%d",&T);
	while(T--)
	{
		clear();
		scanf("%d%d",&m,&n);
		for(int i=1,k;i<=n;i++)
		{
			string opt,str;
			cin>>opt;
			type[i]=mp[opt];
//			printf("%d\n",type[i]);
			scanf("%d",&k);
			while(k--)
			{
				cin>>str;
//				cout<<str<<endl;
				int id=getnum(str);
//				printf("%d\n",id);
				if(str[0]=='I')
				{
					add(id+n,i);
					degree[i]++;
				}
				else
				{
					add(id,i);
					degree[i]++;
				}
			}
		}
//		printf("%d %d\n",m,n);
		scanf("%d",&s);
		vector<int> in[maxn],que[maxn];
		for(int i=1,x;i<=s;i++)
		{
			for(int j=1;j<=m;j++)
			{
				scanf("%d",&x);
				in[i].push_back(x);
			}
		}
		for(int i=1,x,ss;i<=s;i++)
		{
			scanf("%d",&ss);
			while(ss--)
			{
				scanf("%d",&x);
				que[i].push_back(x);
			}
		}
		queue<int> q;
		vector<int> vec[maxn],ord;
		bool loop=false;
		for(int i=1;i<=n+m;i++)
		{
			if(!degree[i])
			{
				q.push(i);
			}
		}
		while(!q.empty())
		{
			int x=q.front();q.pop();
			ord.push_back(x);
			for(int i=head[x];i;i=edge[i].next)
			{
				if(!(--degree[edge[i].to]))
				{
					q.push(edge[i].to);
				}
				vec[edge[i].to].push_back(x);
			}
		}
		for(int i=1;i<=n+m;i++) if(degree[i]) loop=true;
		if(loop) printf("LOOP\n");
		else
		{
//			printf("%d\n",ord.size());
//			for(int i=0;i<ord.size();i++) printf("%d ",ord[i]);printf("\n");
			for(int t=1;t<=s;t++)
			{
				for(int i=1;i<=m;i++) sta[i+n]=in[t][i-1];
//				for(int i=0;i<(int)ord.size();i++) printf("#%d %d\n",ord[i],vec[ord[i]].size());
				for(int i=0;i<(int)ord.size();i++)
				{
					if(ord[i]>n) continue;
					if(type[ord[i]]==1)
					{
						sta[ord[i]]=!sta[vec[ord[i]][0]];
					}
					else if(type[ord[i]]==2)
					{
						sta[ord[i]]=sta[vec[ord[i]][0]];
						for(int j=1;j<vec[ord[i]].size();j++)
						{
							sta[ord[i]]&=sta[vec[ord[i]][j]];
						}
					}
					else if(type[ord[i]]==3)
					{
						sta[ord[i]]=sta[vec[ord[i]][0]];
						for(int j=1;j<vec[ord[i]].size();j++)
						{
							sta[ord[i]]|=sta[vec[ord[i]][j]];
						}
					}
					else if(type[ord[i]]==4)
					{
						sta[ord[i]]=sta[vec[ord[i]][0]];
						for(int j=1;j<vec[ord[i]].size();j++)
						{
							sta[ord[i]]^=sta[vec[ord[i]][j]];
						}
					}
					else if(type[ord[i]]==5)
					{
						sta[ord[i]]=sta[vec[ord[i]][0]];
						for(int j=1;j<vec[ord[i]].size();j++)
						{
							sta[ord[i]]&=sta[vec[ord[i]][j]];
						}
						sta[ord[i]]=!sta[ord[i]];
					}
					else if(type[ord[i]]==6)
					{
						sta[ord[i]]=sta[vec[ord[i]][0]];
						for(int j=1;j<vec[ord[i]].size();j++)
						{
							sta[ord[i]]|=sta[vec[ord[i]][j]];
						}
						sta[ord[i]]=!sta[ord[i]];
					}
				}
				for(int i=0;i<que[t].size();i++) printf("%d ",sta[que[t][i]]);
				printf("\n");
			}
		}
	}
	return 0;
}

第四题(仅作参考)

题目大意

给出 n n n个点,再给出一个半径为 r r r的圆/球/超球体(这里记为CIRCLE)。对于每个点,需要求出这个点与其他点的最短距离之和,不能经过CIRCLE

解法

对于两个点连线不经过CIRCLE的一对点,最近距离就是直接连线。

对于两个点连线经过CIRCLE的一对点,最近距离就是切线+圆弧+切线。

但是这道题调了3个小时也不对,不知道是写挂了还是思路有问题。
最后只过了40分的所有点都在圆上的情况。

代码(40分)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const double pi=acos(-1.0);
const int maxn=105;
int n,m,r,c[maxn],p[2005][maxn];
inline double getdis(int x,int y)
{
	return 0;
}
void work15()
{
	for(int i=1;i<=m;i++)
	{
		double temp=0;
		for(int j=1;j<=m;j++)
		{
			if(i==j) continue;
			double dis=sqrt((double)(p[i][1]-p[j][1])*(p[i][1]-p[j][1])+(double)(p[i][2]-p[j][2])*(p[i][2]-p[j][2]));
			double tdis=dis/2.0;
			double sinv=tdis/(double)r;
//			printf("@%lf\n",sinv);
			double ta=asin(sinv)*2;
//			printf("#%lf\n",ta/pi);
			temp+=ta*r;
		}
		printf("%.14lf\n",temp);
	}
}
int main()
{
	#ifdef lemon
	freopen("D.txt","r",stdin);
	#endif
	scanf("%d%d%d",&n,&m,&r);
	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++) scanf("%d",&p[i][j]);
	if(m<=50) work15();
	else
	{
		for(int i=1;i<=m;i++)
		{
			double temp=0;
			for(int j=1;j<=m;j++)
			{
				if(i==j) continue;
				double tt=0;
				for(int k=1;k<=n;k++) tt+=(double)(p[i][k]-p[j][k])*(p[i][k]-p[j][k]);
				double dis=sqrt(tt);
				double tdis=dis/2.0;
				double sinv=tdis/(double)r;
	//			printf("@%lf\n",sinv);
				double ta=asin(sinv)*2;
	//			printf("#%lf\n",ta/pi);
				temp+=ta*r;
			}
			printf("%.14lf\n",temp);
		}
	}
	return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值