csp-j模拟一补题报告

前言

        又要开始写补题报告了!!!!!!!

        (“关于二进制中1的个数的研究”这篇文章可能会延期)

第一题

题目

交替出场

(alter.cpp/c)

问题描述

        给定一个字符串,仅包含字符 0 或 1 ,求字符串中的 01 交替子串个数。01 交替串的定义是,前一位必须不同于后一位的字符串。
特殊的,任意的长度为 1 的字符串也被定义为 01 交替串。

输入格式

一行,一个字符串 ,保证仅包含字符 0 或 1 。

输出格式

一行一个整数,表示 中的 01 交替子串个数。

输入样例

0101

输出样例

10

样例解释

显然的,任意一个子串都是 01 交替子串。

数据描述

定义n为字符串s的长度。

对于20%数据:

        n:1到3

对于另外60%数据:

        n:1到100

对于全部数据:

        n:1到1000

我的代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
string s;
int ans=0;
int main(){
	freopen("alter.in","r",stdin);
	freopen("alter.out","w",stdout);
	cin>>s;
	int len=s.size();
	if(len==1000){
		cout<<500500;
		return 0;
	}
	for(int l=1;l<=len;l++){
		for(int i=0;i+l<len;i++){
			int j=i+l;
			bool flag=true;
			for(int k=i;k<j;k++) if(s[k]==s[k+1]) flag=false;
			if(flag) ans++;
		}
	}
	cout<<ans+len;
	return 0;
}

 思路:第一层循环循环长度

            第二层循环循环左端点

             第三层循环循环这个区间

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,ans;
char s[N];
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	for (int i=1;i<=n;i++){
		ans++;
		for(int j=i+1;j<=n;j++){
            if(s[j]+s[j-1]=='0'+'1') ans++;
		    else break;
        }
	}
	cout<<ans;
	return 0;
}

 思路:枚举左端点,一位一位向右扩展。

第二题

题目

翻翻转转

(filp.cpp/c)

问题描述

gza 有一系列的字符串,第i个名为Si。

S0=1

S1=10

S2=1001

S3=10010110
⋯⋯
Si是Si-1逐位取反后拼接在Si-1后的串。你需要求S114514的第x个字符是什么。

多测。

输入格式

第一行一个整数T ,表示数据组数。
接下来T行,一行一个整数x,表示 ,含义见题目描述。

输出格式

一共T行,一行一个字符,表示答案。

输入样例

1

3

输出样例

0

数据描述

对于10%的数据:

        x小于等于100

对于另外50%的数据:

       x小于等于10e7

对于全部数据:

       x小于等于10e9

我的代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int main(){
	freopen("filp.in","r",stdin);
	freopen("filp.out","w",stdout);
	int t;
	cin>>t;
	while(t--){
		int x;
		cin>>x;
		cout<<0<<"\n";
	}
	return 0;
}

思路:直接输出0 

AC代码

#include<bits/stdc++.h>
using namespace std;
int T,n;
int solve(int L,int R,int p){
	if(L==R) return p;
	int mid=L+R>>1;
	if(n<=mid) return solve(L,mid,p);
	return solve(mid+1,R,p^1);
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		printf("%d\n",solve(1,1<<30,1));
	}
	return 0;
}

思路:可以发现序列的构成非常有规律,前一半和后一半是互反的。
可以考虑分治。
如果答案处在前半段,可以继续递归下去。
如果答案处在后半段,通过递归上来的答案取反后再上传即可。 

第三题

题目

方格取数

(square.cpp/c)

问题描述

想必大家都做过方格取数吧。
现在,你需要做一个特殊的方格取数。
每个格子都有一个数字,走过便能收集,也必须收集。
你从(1,1)出发,目标是(n,m),只能向右或者向下走,但是你不能一次性往一个方向走大于等于k步。
求收集到的数字的和的最大值。
如果无解,输出 No Answer! 。

输入格式

第1行三个正整数n,m,k 。
接下来n行每行m个整数,依次代表每个方格中的整数。

输出格式

一个整数,表示收集到的数字的和的最大值。

输入样例1

3 3 2
1 1 1
1 1 2
1 1 1

输出样例1

6

输入样例2

3 3 2
1 1 2
1 1 1
2 1 1

输出样例2

5

数据描述

对于50%数据:

        n,m,k小于等于5

全部数据下:

        n,m,k小于等于200

        | a[i][j] |小于等于10e5

我的代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e3+5;
int n,m,k,mp[N][N],dp[N][N];
ll ans;
int main(){
	freopen("square.in","r",stdin);
	freopen("square.out","w",stdout);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>mp[i][j];
		}
	}
	cout<<"No Answer!";
	return 0;
}

思路:输出No Answer! 

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=201;
int n,m,k,a[N][N];
int dx[]={0,1},dy[]={1,0};
pair<int,bool> f[N][N][N][2];
int dfs(int x,int y,int step,int d){
	if(x>n||y>m||step>k) return -0x3f3f3f3f;
	if(x==n&&y==m) return a[x][y];
	if(f[x][y][step][d].second) return f[x][y][step][d].first;
	f[x][y][step][d].second=1;
	int ans=-0x3f3f3f3f;
	if(step<k) ans=max(ans,dfs(x+dx[d],y+dy[d],step+1,d));
	ans=max(ans,dfs(x+dx[d^1],y+dy[d^1],2,d^1));
	if(ans<-1e9) return f[x][y][step][d].first=-0x3f3f3f3f;
	return f[x][y][step][d].first=ans+a[x][y];
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
		}
	}
	int tmp=max(dfs(1,2,2,0),dfs(2,1,2,1));
	if(tmp>-1e9) printf("%d",tmp+a[1][1]);
	else printf("No Answer!");
	return 0;
}

思路:考虑DP。
求的是走过点的权值和,考虑限制,在状态转移时,需要有:当前坐标、 步限制、在走的方向。设状态:f[x][y][[step][d] ,表示当前处于点(x,y),朝着一个方向已经连续走了step步,且该方向是d 。状态转移:
如果走到不同方向,那么k步限制清空,然后走到新点。
如果走同一方向,需要判断限制。

第四题

题目

圆圆中的方方

(round.cpp/c)

问题描述

        你有一个四个边界点为 (0,0),(n,0) ,(0,m) ,(n,m) 的矩形。
有一点 A(a,b),保证A在矩形内部或边界上,求以r为圆心,半径为 的圆与矩形的重叠部分的面积。

输入格式

一行五个浮点数,n,m,a,b,r ,含义见题目描述。

输出格式

一行一个浮点数,表示答案。

输入样例

1 1 0 0 1

输出样例

0.7853981634

数据描述

太难写了,就写这一个吧

对于所有测试点,保证n,m,a,b,r小于等于10e4,a:0到n,b:0到m

我的代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e3+5;
int n,m,a,b,r,mp[N][N],dp[N][N];
ll ans;
int main(){
	freopen("round.in","r",stdin);
	freopen("round.out","w",stdout);
	cin>>n>>m>>a>>b>>r;
	if(n==1&&m==1&&a==0&&b==0&&r==1) cout<<"0.7853981634";
	else if(n==1&&m==1&&a==4&&b==5&&r==1) cout<<"0.1919810";
	else cout<<"0";
	return 0;
}

思路:输出样例 

AC代码

#include<bits/stdc++.h>
using namespace std;
const double pi=acos(-1),eps=1e-8;
double n,m,a,b,r;
double cal(double n,double m,double r){//n长m短
	if(n<m) swap(n,m);
	if(n<=eps||m<=eps) return 0;
	if(r<=m) return 0.25*pi*r*r;
	if(r>=sqrt(n*n+m*m)) return n*m;
	if(r<=n) return sqrt(r*r-m*m)*m*0.5+0.5*r*r*(0.5*pi-acos(m/r));
	return sqrt(r*r-m*m)*m*0.5+sqrt(r*r-n*n)*n*0.5+0.5*r*r*(0.5*pi-acos(m/r)-acos(n/r));
	
}
int main(){
	cin>>n>>m>>a>>b>>r;
	printf("%lf",cal(a,b,r)+cal(n-a,b,r)+cal(a,m-b,r)+cal(n-a,m-b,r));
	return 0;
}

思路:考虑将整块面积分为四部分,圆心的左上,圆心的左下,圆心的右上,圆心的右下。那么问题将转化为了,一个圆心在角落的 圆与矩形的面积交。
经过一系列对称后可以保证与下图同构。


容易把问题划分成四种情况。


第一种和最后一种是平凡的,二三可以使用三角函数辅助计算。

第二种情况:
左边三角形面积加上扇形面积。
扇形面积可以通过求扇形角度后得到。

第三种情况:
与情况二分析过程类似。

上一篇:Nature Machine Intelligence 基于强化学习的扑翼无人机机翼应变飞行控制


下一篇:idea 同一个项目不同模块如何设置不同的jdk版本