【20200603程序设计思维与实践 CSP-M4&补题】

目录

    • TT数鸭子
      • 题意
      • 思路
      • 代码
    • ZJM要抵御宇宙射线
      • 题意
      • 思路
      • 思考
      • 代码
    • 宇宙狗的危机
      • 题意
      • 思路
      • 思考
      • 代码


TT数鸭子

题意

【20200603程序设计思维与实践 CSP-M4&补题】_第1张图片


思路

检查每只鸭子映射成的数,用一个元素数为符号数(10)的布尔数组记录出现过的数符,累加得到该数中的数符种类然后判断是否小于k。


代码

#include
#include
using namespace std;

int n,k;
long long cot[15];
char str[20];
int ans;

void clean(){
	for(int i=0;i<=9;i++)cot[i]=0;
} 

int main(){
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++){
		clean();
		scanf("%s",str);
		int len=strlen(str);
		for(int i=0;i<len;i++)
			cot[str[i]-'0']++;
		int temp=0;
		for(int i=0;i<=9;i++)
			if(cot[i]!=0)temp++;
		if(temp<k)ans++;
	}
	printf("%d\n",ans);
}

ZJM要抵御宇宙射线

题意

【20200603程序设计思维与实践 CSP-M4&补题】_第2张图片


思路

本题并不用找最小圆覆盖,只要暴力检查所有点即可,可在O(n2)内完成。

维护一个全局最小值GlobalMin和局部最大值PartMax。对于每个点,检查它与其他所有点之间距离的平方,并将最大值记录在PartMax中,检查完后与GlobalMin比较,取较小值并相应更新选择的点。最终答案即GlobalMin与其对应的中心点。


思考

本题爆了一个点,因为最大值算错了一个常数,开的大小不足。

本题要求距离的平方,结果保留两位小数,而坐标都是整数,所以取巧直接格式输出"%lld.00"。


代码

#include
#include
#define MAX 100000000000//最大值开错了一个常数级 
using namespace std;

struct point{
	long long x,y;
	bool operator<(const point& p)const{
		if(x!=p.x)return x<p.x;
		return y<p.y;
	}
};

long long dist2(point a,point b){
	long long x2=(b.x-a.x)*(b.x-a.x);
	long long y2=(b.y-a.y)*(b.y-a.y);
	return x2+y2;
}

int N;
point P[1005];
long long GlobalMin,PartMax;
int grcd;

int main(){
	scanf("%d",&N);
	//cin>>N;
	for(int i=1;i<=N;i++){
		long long x,y;
		scanf("%lld %lld",&x,&y);
		P[i].x=x;
		P[i].y=y;
	}
	sort(P+1,P+N+1);
	GlobalMin=MAX;
	for(int i=1;i<=N;i++){
		PartMax=0;
		for(int j=1;j<=N;j++){
			if(j!=i){
				long long temp=dist2(P[i],P[j]);
				if(temp>PartMax)PartMax=temp;
			}
		}
		if(PartMax<GlobalMin){
			GlobalMin=PartMax;
			grcd=i;
		}
	}
	printf("%lld.00 %lld.00\n%lld.00\n",P[grcd].x,P[grcd].y,GlobalMin);
}

宇宙狗的危机

题意

【20200603程序设计思维与实践 CSP-M4&补题】_第3张图片


思路

本题运用动态规划。

定义状态L[i][j]R[i][j],分别表示"[j,i-1]能成为i的左子树"、"[i+1,j]能成为i的右子树"。预处理二维数组E[i][j]表示"gcd(a[i],a[j])>1"。

状态转移过程如下:对每一个区间[i,j],检查向左[i-1,j]扩展和向右[i,j+1]扩展,方法是检查区间[i,j]中是否存在节点k,满足L[i,k]&&R[k,j]&&E[i-1][k]L[i,k]&&R[k,j]&&E[j+1][k]。遍历所有的区间[i,j],获得全部的L[i][j]和R[i][j],最后检查是否存在L[k][1]&&R[k][n]==1,若存在即Yes。


思考

课堂测试中其实没有看出来这是一道dp题,用了一种错误的贪心做法:
对于每一个要添加的节点,首先检查其与当前树最大节点的gcd是否大于1,若是则直接插入,否则从根节点开始沿右子检查,找到第一个与要添加节点的gcd大于1的节点,因为其必无左子,将父节点作为左子插入,将自身作为新节点的右子插入,重构搜索树。

后来找到一个反例:在此类情况下,搜索树存在,但该做法不能实现
【20200603程序设计思维与实践 CSP-M4&补题】_第4张图片
(17×29不能紧随13×23插入,结果为No)


代码

#include
#include
using namespace std;

int t,n,a[705];
bool JDG;
bool E[705][705];
bool L[705][705],R[705][705];

int gcd(int a,int b){return b==0?a:gcd(b,a%b);}

int main(){
	cin>>t;
	while(t--){
		memset(L,0,sizeof(L));
		memset(R,0,sizeof(R));
		JDG=0;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			E[i][i]=0;
			L[i][i]=R[i][i]=1;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(i!=j){
					if(gcd(a[i],a[j])>1)E[i][j]=1;
					else E[i][j]=0;
				}
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=i;j>=1;j--){
				for(int k=j;k<=i;k++){
					if(L[k][j]&&R[k][i]){
						if(E[i+1][k])L[i+1][j]=1;
						if(E[j-1][k])R[j-1][i]=1;
					}
				}
			}
		}
		for(int i=1;i<=n;i++){
			if(L[i][1]&&R[i][n]){
				JDG=1;
				break;
			}
		}
		if(JDG)cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
}

你可能感兴趣的:(【20200603程序设计思维与实践 CSP-M4&补题】)