模拟赛一补题报告

得分情况

题目交替出场(alter)翻翻转转(filp)方格取数(square)圆圆中的方方(round)
赛中得分100102020
赛后得分10010010060

      

题目分析:

        T1:交替出场(alter)

           

题目描述

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

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

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

输入描述

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

输出描述

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

输入样例

0101

输出样例

10

样例解释

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

数据范围

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

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

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

题目大意

给定一个01字符串,求它的前一位必须不同于后一位的子串个数(长度为1的也算是一个交替字串)

题目分析

国服水题。

解释一下思路

比如000001

在这个字符串中,首先从s[0]也就是0开始枚举,满足,ans++,接着开始访问s[0]+s[1],发现已经不满足了,这时候无论再往后找多少个数一定都是不满足了,所以可以直接break走,只需要双层循环O(n^2)就能直接AC

正解代码:
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
int main(){
	string a;
	cin>>a;
	int len=a.size(),ans=len;
	for(int i=0;i<len-1;i++){
		for(int j=i+1;j<len;j++){
			if(a[j]!=a[j-1])ans++;
			else break;
		}
	}cout<<ans;
	return 0;
}

蒟蒻实在是懒得写酷炫的freopen,不写了ヾ(≧▽≦*)o

T2:翻翻转转(filp)

题目描述

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

s0=1

s1=10

s2=1001

s3=10010110

⋯⋯

s​i​​ 是 si−1 逐位取反后拼接在 s​i−1​​ 后的串。

你需要求 s114514​​ 的第 x 个字符是什么。

多测。

输入描述

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

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

输出描述

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

输入样例

1

3

输出样例
0
数据范围

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

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

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

题目大意

就是把每一位取反后拼在后面,然后求第x位

解题思路

我当时就瞎写了一个十分逆天的代码,就是说所有为0的位置规律是每次加1、2、3、1、3、2,然后就打表,赶出了一串让我们震惊两百辈子的代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int w[10]={1,2,3,1,3,2};
map<ll,bool>vis;
int main(){
	//freopen("filp.in","r",stdin);
	//freopen("filp.out","w",stdout);
	int t;
	cin>>t;
	int ccnt=0;
	for(int i=2;i<=1000000;i+=w[ccnt],ccnt++){
		vis[i]=true;
		if(ccnt==6)ccnt=0;
	}while(t--){
		int x;
		cin>>x;
		if(vis[x]==true)cout<<"0\n";
		else cout<<"1\n";
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
} 

神奇的错误代码,应该是属于运气好骗了10分(样例能过)这种逆天代码貌似根本不需要解释就能看出他的奇葩。

正解思路

(位运算是个好东西)

我们通过观察可以发现一些东西:

dp[2]=!dp[1] 

dp[3]=!dp[1]

dp[4]=!dp[2]

dp[5]=!dp[1]

dp[6]=!dp[2]

dp[7]=!dp[3]

dp[8]=!dp[4]

再结合一点小科技

dp[2]=!dp[1]      s1

dp[3]=!dp[1]      dp[4]=!dp[2]    s2

dp[5]=!dp[1]      dp[6]=!dp[2]     dp[7]=!dp[3]     dp[8]=!dp[4]   s3

可以发现, s几,它所取反的数就与它相差2^(几-1)

而每一个最终结果不是!dp[1]的都可以接着分,这样就可以不科学的求出正解

那么怎么求呢?

也是非常困难简单

如果这个数=2^n,那么它对应的应该是2^(n-1)

如果这个数<2^n(它属于sn),那么它对应的应该是n-2^n

怎么求出来这个s?,直接log2就完事了

OK,那么

代码,启动!

正解代码 
#include<bits/stdc++.h>
#define ll long long
using namespace std;
bool fun(int n){
	if(n==1)return 1;
	int k=log2(n);
	if(1<<k==n)return !fun(1<<(k-1));
	return !fun(n-(1<<k)); 
}
int main(){
	//freopen("filp.in","r",stdin);
	//freopen("filp.out","w",stdout);
	int t;
	cin>>t;
	while(t--){
		int x;
		cin>>x;
		cout<<fun(x)<<endl;
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
} 

T3:方格取数(square)

题目描述

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

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

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

你从 (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,∣ai,j∣≤10^5​​

 题目大意

只能往下或右走,从(1,1)到(n,m),不能连续往下或右≥k次,求最大权值。

解题思路

最逆天的是,我比赛时竟然没看见我们敬爱的“ No Answer!",因此我快乐的GameOver

思路就是dfs求最大值

错误示范

#include<bits/stdc++.h>
using namespace std;
int a[205][205];
int n,m,k;
int dfs(int x,int y,int l,int op){
	if(x==n&&y==m)return a[x][y];
	if(x>n||y>m)return 0;
	if(l==k-1&&op==0)return a[x][y]+dfs(x+1,y,0,1);
	if(l==k-1&&op==1)return a[x][y]+dfs(x,y+1,0,0);
	int ii=dfs(x+1,y,(op==1?l+1:0),1);
	int jj=dfs(x,y+1,(op==0?l+1:0),0);
	return a[x][y]+max(ii,jj);
}
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>>a[i][j];
		}
	}cout<<max(dfs(1,1,0,1),dfs(1,1,0,0));
	//fclose(stdin);
	//fclose(stdout);
	return 0;
} 

正解思路

这边先说60分代码

考虑dfs。

不能连续往一个方向走k步,刷新方向后步数会重置
由于是在一个矩阵图形中进行移动并计算权值和,可以使用dfs
搜索或遍历所有的路径,计算所有路径的权值和
需要额外记录按照一个方向走的步数和当前权值和(可能有负数)

废话不多说,代码,启动!!!!!!!!!!!!!!!!!

#include<iostream>
using namespace std;
typedef long long ll;
const ll N=200,MIN=-1e18;
ll n,m,k,a[N+5][N+5],ans=MIN;
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;
	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;
}

骗60还是好点的,但是100就非常的神奇(不科学)了

(dp是个好东西)

三维dp

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

很抽象,但是动规就是一个抽象的东西(我也很讨厌这鬼玩意)所以直接

代码,启动!!!!!!!

正解代码

#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;
int main(){
	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][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][0]=maxr;
		}
	}
	ans=max(dp[n][m][1],dp[n][m][0]);
	if(ans<=-99600000)	cout<<"No Answer!";
	else cout<<ans;
	return 0;
} 

T4:圆圆中的方方(round)

题目描述

你有一个四个边界点为 (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。

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

对于测试点 1,保证数据同样例。

对于测试点 2,保证 r≥√​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。

题目大意

 给定了一个圆和一个矩形,求两个面积交。

解题思路

直接骗第一个点

(代码懒得写上了)

正解思路

sub1
就是样例,送分。
sub2
给出了条件,说明半径长度超过了矩形对角线长,因此,矩形完全包含在了圆中。答案
就是矩形面积。
sub3
半径满足:
,说明整个圆都包含在了矩形中,因此圆的面积就是答案。
sub4、sub5
需要考虑到正解,但是因为细节实现复杂,设置这部分分。
sub6
考虑将整块面积分为四部分,圆心的左上,圆心的左下,圆心的右上,圆心的右下。那么问题将转化为了,一个圆心在角落的 圆与矩形的面积交。
经过一系列对称后可以保证与下图同构。


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


第一种和最后一种是平凡的,二三可以使用三角函数辅助计算。第二种情况:
左边三角形面积加上扇形面积。
扇形面积可以通过求扇形角度后得到。
第三种情况:
与情况二分析过程类似。

正解代码

#include <bits/stdc++.h>
using namespace std;
const double pi = acos(-1), eps = 1e-6;
double n, m, x, y, r;
double calc(double n, double m, double r) {
    if (n > m)
        swap(n, m);
    if (n < eps || m < eps)
        return 0;
    if (r <= n)
        return 0.25 * pi * r * r;
    else if (r <= m) {
        double tt = sqrt(r * r - n * n);
        double res = n * tt / 2.0;
        double ang = pi / 2-acos(n / r);
        res += ang / 2 * r * r;
        return res;
    } else if (r <= sqrt(n * n + m * m)) {
        double t1 = sqrt(r * r - n * n), t2 = sqrt(r * r - m * m);
        double res = n * t1 / 2.0 + m * t2 / 2.0;
        double ang = pi / 2 - acos(n / r) - acos(m / r);
        res += ang / 2 * r * r;
        return res;
    } else
        return n * m;
    }
int main() {
    cin >> n >> m >> x >> y >> r;
    cout << fixed << setprecision(10)<< calc(x, y, r) + calc(x, m - y, r) + calc(n - x, y, r) +calc(n - x, m - y, r)<< endl;
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值