CSP-J 复赛模拟赛~~~李奕泽补题报告

一、题目得分:    

T1 交替出场    AC     
T2 翻翻转转    AC  
T3 方格取数      10    
T4 圆圆中的方方    10

二、赛中情况

        第一道题想了一会儿就开始敲代码,很快便把第一道题敲完了,考察区间。
   
        第二道题本来想打暴力,但数据范围是10的九次方,直接超时,最多得60,淘汰。发现s[i]等于i减第一个小于它的2进制数取反(s[i] = ~s[i - 2^x]),所以先处理出2^i的数,然后就通过关系知道1与i的关系,解决。
        第三道题用dfs,得了10分,有人暴力得了50分,想法对了,但没对
        第四道题本想偏分,期望得30,结果得10。

三、题目分析:

交替出场(alter)

 题目描述

给定一个字符串,仅包含字符 0 或 1,求字符串中的 01 交替子串个数。

01 交替串的定义是,前一位必须不同于后一位的字符串。

!!!特殊的,任意的长度为 1 的字符串也被定义为 01 交替串。

输入描述

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

输出描述

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

输入样例

0101

输出样例

10

样例解释

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

数据范围

定义 nn 为字符串 ss 的长度。
对于 20%20% 数据:1≤n≤31≤n≤3。

对于另外 60%60% 数据:1≤n≤1001≤n≤100。

对于全部数据:1≤n≤10001≤n≤1000。

#include<iostream>
#include<cstdio>
using namespace std;
int main(){
	string s;
	cin >> s;
	int cnt = 1,sum = 0;
	for(int i = 1;i < s.length();i++){
		if(s[i] != s[i-1]){
			cnt ++;
		}else{
			for(int i = 1;i <= cnt;i++){
				sum += i;
			}
			cnt = 1;
		}
	}
	for(int i = 1;i <= cnt;i++){
		sum += i;
	}
	printf("%d",sum);
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

如果一个子串都是由 01 交替子串构成,那这个字符串就用1+2+3+···+字符串的长度。而字符串可以分解成多个子串,所以求出它的各个由 01 交替子串构成的子串相加就行。

        

翻翻转转(filp)

题目描述

XXX有一系列的字符串,第 ii 个名为 sis​i​​。

s0=1s​0​​=1

s1=10s​1​​=10

s2=1001s​2​​=1001

s3=10010110s​3​​=10010110

⋯⋯

si 是 si-1 逐位取反后拼接在 si−1后的串。

你需要求 s​114514​​ 的第 xx 个字符是什么。

多测。

输入描述

第一行一个整数 TT,表示数据组数。

接下来 TT 行,一行一个整数,表示 xx,含义见题目描述。

输出描述

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

输入样例

1
3

输出样例

0

数据范围

对于 10%10% 的数据:x≤100x≤100。

对于另外 50%50% 的数据:x≤107x≤10​7​​。

对于全部数据:x≤109x≤10​9​​。

先写样例:

1 1

2 10

3 1001

4 1001 0110

5 1001 0110 0110 1001

6 1001 0110 0110 1001 0110 1001 1001 0110

······

 假设x=10

s[10]=!s[10-8] = !s[2]

s[2]=!s[2-1]=!s[1]

所以 s[10]=1

假设x=19

s[19]=!s[19-16]=!s[3]

s[3]=!s[3-2] = !s[1]

所以 s[19]=1

重点来了!!!

递推:

//arr里存2的次方数

for(int i = 30;i >= 0;i--){

    if(arr[i] < x){
    x =x - arr[i];
        if(cnt == 0){
            cnt = 1;
        }else{
            cnt = 0;
        }
    }

}

#include<iostream>
#include<cstdio>
using namespace std;
long long arr[50];
int main(){
	arr[0] = 1;
	arr[1] = 2;
	for(int i = 2;i <= 33;i++){
		arr[i] = arr[i-1] * 2;
	}
	long long T;
	scanf("%lld",&T);
	while(T--){
		long long x,cnt = 0;
		scanf("%lld",&x);
		for(int i = 30;i >= 0;i--){
			if(arr[i] < x){
				x -= arr[i];
				if(cnt == 0){
					cnt = 1;
				}else{
					cnt = 0;
				}
			}
		}
		if(cnt == 1){
			printf("%d\n",0);
		}else{
			printf("%d\n",1);
		}	
	}
	
	
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

 不懂看上文↑↑↑

方格取数(square)

题目描述

想必大家都做过方格取数吧。

现在,你需要做一个特殊的方格取数。

每个格子都有一个数字,走过便能收集,也必须收集。

你从 (1,1) 出发,目标是 (n,m),只能向右或者向下走,但是你不能一次性往一个方向走大于等于 kk 步。

求收集到的数字的和的最大值。

如果无解,输出 No Answer!

输入描述

第 11 行三个正整数 n,m,kn,m,k。

接下来 nn 行每行 mm 个整数,依次代表每个方格中的整数。

输出描述

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

输入样例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%50% 数据:n,m,k≤5

全部数据

下:n,m,k≤200,∣a​i,j​​∣≤10​5​​

这道题正解用DP,用DFS能得60分。这不是我的

不懂看题解

#include<iostream>
using namespace std;
typedef long long ll;
const int N=200,MIN=-1e9;
ll n,m,k,a[N+5][N+5],dp[N+5][N+5][2],ans=MIN;
/*不能连续往一个方向走k步,刷新方向后步数会重置
由于是在一个矩阵图形中进行移动并计算权值和,可以使用dfs
搜索或遍历所有的路径,计算所有路径的权值和
需要额外记录按照一个方向走的步数和当前权值和(可能有负数)
void dfs(int x,int y,int down,int right,ll sum){
	if(x==n&&y==m){
		ans=max(ans,sum);
		return ;
	}
	if(x<n && down+1<k)	dfs(x+1,y,down+1,0,sum+a[x+1][y]);
	if(y<m && right+1<k)dfs(x,y+1,0,right+1,sum+a[x][y+1]);
} 
int main(){
	cin>>n>>m>>k;	n=m=k=200;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)	cin>>a[i][j]; 
	}
	dfs(1,1,0,0,a[1][1]);
	if(ans==-1e18)	cout<<"No Answer!";
	else cout<<ans;
	return 0;
}
*/
int main(){
/*dp[i][j][1/0]:到达第i行,第j列所经过数字之和的最大值,
	其中1代表是从上往下到达i,j,0代表从左往右到达i,j
到达第i行,第j列:
	1、可能是从上往下,每次走1步,连走1步,2步,3步...k步;
	所以设d[t]:代表从上往下,连走t步所经过的数字之和的最大值
	由此可得d[t] = dp[i - t][j] + sumt;
			//sumt代表从第i - t + 1行到第i行的所有数字之和;
			dp[i][j][1] = max(d[t]);
	2、可能是从左往右,每次走1步,连走1步,2步,3步...k步;
	所以设r[t]:代表从左往右,连走t步所经过的数字之和的最大值
	由此可得r[t] = dp[i][j - t] + sumr;
			//sumr代表从第j - t + 1列到第j列所有数字之和;
			dp[i][j][0] = max(r[t]);
所以ans = max(dp[i][j][1],dp[i][j][0]);
		1 <= t <= k - 1
*/	
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
			dp[i][j][1]=dp[i][j][0]=MIN;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(i==1&&j==1){
				dp[i][j][1]=dp[i][j][0]=a[1][1];//处理起点
				continue; 
			}
			ll sumd=0,sumr=0,maxd=MIN,maxr=MIN;
			for(int t=1;i-t>=1&&t<=k-1;t++){	//从上往下
				sumd+=a[i-t+1][j];
				maxd=max(maxd,dp[i-t][j][0]+sumd);
				//dp[i - t][j][0]代表从左到右到达i - t,j
			}
			dp[i][j][1]=maxd;
			for(int t=1;j-t>=1&&t<=k-1;t++){	//从左往右 
				sumr+=a[i][j-t+1];
				maxr=max(maxr,dp[i][j-t][1]+sumr);
				//dp[i][j - t][1]代表从上到下到达i,j - t
			}
			dp[i][j][0]=maxr;
		}
	}
	ans=max(dp[n][m][1],dp[n][m][0]);
	if(ans<=-99600000)	cout<<"No Answer!";
	else cout<<ans;
	return 0;
} 

 

 

圆圆中的方方(round)

上一题下一题

题目描述

我的提交

题解

时间限制:1秒        内存限制:256M

题目描述

你有一个四个边界点为 (0,0),(n,0),(0,m),(n,m) 的矩形。

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

输入描述

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

输出描述

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

输入样例

1 1 0 0 1

输出样例

0.7853981634

数据范围

设答案为 a,输出为 b。

若 max(1,b)​​∣a−b∣​​≤10​−4​​,则给予这个测试点的分数。

对于测试点 1,保证数据同样例。//直接输出,有20分

对于测试点 2,保证 r>=sqrt(n^2+m^2) // 人话:说明半径长度超过了矩形对角线长,圆包含长方形

对于测试点 3,保证 r≤min(a,b,n−a,m−b)。//人话:长方形包含圆

对于测试点 4∼6,保证 n,m,a,b,r≤10。

对于测试点 7,保证 a=b=0。// 画图

容易把问题划分成四种情况。容易把问题划分成四种情况。
第一种和最后一种是平凡的,二三可以使用三角函数辅助计算。第二种情况:
左边三角形面积加上扇形面积。
扇形面积可以通过求扇形角度后得到。

 第二种情况:

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

对于所有测试点,保证 n,m,a,b,r≤10​4​​,0≤a≤n,0≤b≤m。


 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值