[CSP-J2019] 数字游戏题解(正经与不正经的都有,十种方法)

本文详细介绍了10种解决CSP-J2019数字游戏的方法,包括字符串扫描、位运算、打表、bitset、取余等正经解法,以及线段树、树状数组、Kruskal和FFT等不正经解法,适合不同水平的编程爱好者参考学习。
部署运行你感兴趣的模型镜像

恭喜你发现了宝藏!本文写了十种方法来解决 [CSP-J2019] 数字游戏!你值得拥有!

先看目录吧。正经的解法适合新手参考,整活方法适合大佬阅读!

目录

 我们先来正经的解法

1、字符串扫描法

2、位运算“与”方法

3、 万物皆可打表法

4、bitset容器法

5、取余法

6、cin.get 法

不正经的解法

7、线段树法

8、树状数组法

9、Kruskal最小生成树法

10、(压轴)FFT快速傅里叶变换法

update 

粉福


 我们先来正经的解法。

这些解法都比较基础。可供新手参考。

1、字符串扫描法:

我们把读入的内容当作字符串。之后进行判断:如果是 ‘1’ 就计数器加一。本方法也可以分为两种方法,一种用 char 还有一个自然使用 string.

char 类型方法如下:

#include<bits/stdc++.h>
using namespace std;
int ans=0;
char s[10];
int main(){
	cin>>s;
	for(int i=0;i<8;i++){//模拟
		if(s[i]=='1'){//判断是否为1
			ans++;//计数器++
		}
	}
	cout<<ans;
	return 0;
}

string 类型方法如下:

#include<bits/stdc++.h>
using namespace std;
int main(){
	string s1;
	int cnt;
	cin>>s1;
	for(int i=0;i<s1.length();i++){ //这里可以换为:for(int i=0;i<8;i++)
		if(s1[i]=='1')cnt++;
	}
	cout<<cnt;
	return 0;
}

2、位运算“与”方法:

输入的内容我们可以通过ASCII码来转换为 int 类型。代码可以这么写:

int x=s1[i]-'0';

那么转为整数能干嘛?我们可以使用与运算。位运算 & 符号的含义是:如果两个相应的二进制位都为1,则该位的结果值为1,否则为0。不过这种方法确实多此一举。

#include<bits/stdc++.h>
using namespace std;
int main(){
    string s1;
    cin>>s1;
    int cnt=0;
    for(int i=0;i<s1.length();i++){//等同于:for(int i=0;i<8;i++)
        int x=s1[i]-'0';//运用ascii码转换为整形。等同于:int x=s1[i]-48;
        if(x&1)cnt++;//1的二进制位是1.所以这句话等同于:if(x==1)cnt++;
    }
    cout<<cnt<<endl;
    return 0;
}

3、 万物皆可打表法:

内存限制250MB,空间足够!最大值为11111111,小于60000000。打表干嘛,愣着啊!

#include <bits/stdc++.h>
using namespace std;
int a[11111115];
int main(){
	int n;
	cin>>n;
	for(int i=0;i<=1;i++)
		for(int j=0;j<=1;j++)
			for(int k=0;k<=1;k++)
				for(int l=0;l<=1;l++)
					for(int m=0;m<=1;m++)
						for(int n=0;n<=1;n++)
							for(int o=0;o<=1;o++)
								for(int p=0;p<=1;p++)
									a[i*10000000+j*1000000+k*100000+l*10000+m*1000+n*100+o*10+p]=i+j+k+l+m+n+o+p;
	cout<<a[n];
	return 0;
}

4、bitset容器法:

bitset是一个二进制容器。其中有一个函数非常666,就是 count() 函数。它的作用是求容器中有几个1。那么这道题就迎刃而解了。

#include<bits/stdc++.h>
using namespace std;
int main(){
    bitset<8> k; //题目说长度8,那就来吧
    cin>>k;
    cout<<k.count();
}

5、取余法:


这个大佬真厉害,思路很清奇,在线膜拜。不难理解吧?

#include<bits/stdc++.h>
using namespace std; 
int main(){
    int f;
    cin>>f;
    cout<<f%9;
    return 0;
}

6、cin.get 法:

其实我也不知道怎么去这种方法名字....

#include<bits/stdc++.h>
using namespace std;
int cnt;//计数器 
int main(){
	for(int i=0;i<8;i++) if(cin.get()=='1') cnt++;
	//用cin.get代替字符——其实可以用getchar.
	cout<<cnt;
	return 0;
} 

不正经的解法:

我们为什么要正常思路解?我们可以不正常一点。

7、线段树法:

输入的内容只含有0和1,那么求出1的个数其实就是求出这个字串里所有数字的和。没错这就是区间和。代码就不贴啦~ 其实我不会写。

8、树状数组法:

思路和法七是一样的,只不过这个我会写。

#include<bits/stdc++.h>
using namespace std;
int i,a[500005],c[500005];
char chr;
int lowbit(int x){
	return x&-x;
}//lowbit
void add(int x,int k){
	while(x<=8){
		c[x]+=k;
		x+=lowbit(x);
	}
}//更新c数组
int sum(int x){//求和 
	int t=0;
	while(x!=0){
		t+=c[x];
		x-=lowbit(x);
	}
    return t;
}
int main(){
	for(i=1;i<=8;i++){
		chr=getchar();
		a[i]=chr-'0';
		add(i,a[i]);
	}//输入
	printf("%d",sum(8)-sum(0));//输出
	return 0;
}

这份代码很简单,不过就 x&-x 这里难以理解点,可以看看这个

9、Kruskal最小生成树法:

假设我们有一个树:

如果我们读入的这个串,它的 i 位代表 0 号节点到 i 号节点的距离。输入到 1 我们就建边,反之跳过。最后来一遍 Kruskal 即可。

1. 首先,我们需要一个结构体,对吧?

#include<bits/stdc++.h>
using namespace std;
struct A{
    int a,b,s;
}a[200005];

2.我们要写 cmp 函数作为 sort 函数的比较器。同时还需要写 find 函数作为并查集的算法。(其实他们没有直接逻辑联系,只不过这里放在一起)。

bool cmp(A x,A y){
    return x.s<y.s;
}
int find(int x){
    return f[x]==x?x:find(f[x]);
}

3.这些预备工作写完,该写输入代码了。如果输入的数字为 1 则建边。这里是把 1 当做了字符。输入完继续初始化(不然会出一些很奇怪的错误)

for(i=1;i<=8;i++){
    c=getchar();
    c=='1'?a[++m].a=0,a[m].b=i,a[m].s=1:c=c;
}//建边
for(i=0;i<=8;i++) f[i]=i;//初始化

4.这里是 Kruskal 主要程序!

for(i=1;i<=m;i++){
    int p=find(a[i].a),q=find(a[i].b);
	if(p==q) continue;
	f[q]=p,t+=a[i].s,s++;
    if(s==n-1) break;
}

5.最后输出 t 即可(话说我好像把所有代码都贴上了)...

10、(压轴)FFT快速傅里叶变换法:

没错这是一个压轴的方法,特别整活,你值得拥有!

一个大佬教我的——这道题确实可以使用 FFT. 首先这个题目可以转化为给出字符串和 1 这个字符串有多少个点可以匹配,我们可以分别做 3 次 FFT 即可就可以通过此题。我们定义字串 S,T 的“距离”为:

d(S,T) = \sum_{i=1}^{n}(S_i-T_i)^2 \times S_i \times T_i

则可以匹配的条件就是:

f_i = d(S,T) = 0

为了方便,我们定义 c 为:\small f_i

那么我们令 T 为 1,对于 S 中每个位置都求出一个 c ,那么问题就是这个 c 怎么求。显然可以将距离的式子拆开,变为由三个 ∑ 组成的式子,而这三个式子正好是多项式的形式,那么我们就可以用 FFT 分别做三次来求出每个 c 的值,最后统计一下就可以出解了。这次我们真的只贴一部分代码(懒得写)

//FFT主代码
inline void FFT(reg Complex *A,reg int opt){
    for(reg int i=0;i<lim;i++) if(i<rev[i]) swap(A[i],A[rev[i]]);
    for(reg int mid=1;mid<lim;mid<<=1){
        reg Complex Wn=((Complex){cos(Pi/(db)mid),(db)opt*sin(Pi/(db)mid)});
        for(reg int j=0;j<lim;j+=(mid<<1)){
            reg Complex W=((Complex){1,0});
            for(reg int k=0;k<mid;k++,W=W*Wn){
                reg Complex x=A[j+k],y=W*A[j+k+mid];
                A[j+k]=x+y;
                A[j+k+mid]=x-y;
            }
        }
    }
}
//注:代码中的 reg 指的是 register.
//运算符重载
struct Complex{
    db x,y;
    friend Complex operator + (const Complex &a,const Complex &b){
        return ((Complex){a.x+b.x,a.y+b.y});
    }
    friend Complex operator - (const Complex &a,const Complex &b){
        return ((Complex){a.x-b.x,a.y-b.y});
    }
    friend Complex operator * (const Complex &a,const Complex &b){
        return ((Complex){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x});
    }
    friend Complex operator * (const Complex &a,const db &val){
        return ((Complex){a.x*val,a.y*val});
    }
}f[2000005],g[2000005],p[2000005];
//注:这里的 db 指的是 double.

update: 

2022.08.09: 文章发布了!

同日:修改了一处笔误并添加了粉福和update区。

同日:增加了两个标签。

粉福:

1、满10粉爆C++码风

2、满15粉爆Python3码风

3、满20粉爆H5码风

(待补充中....)

求赞+关注

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

对不起,由于我是一个文本交互的模型,我无法提供图像内容或直接链接到具体的题解或解决方案。但是,我可以帮你理解CSP-J2019公交换乘问题的基本概念和解决策略,这通常涉及到数据结构、图论以及算法设计。 CSP-J2019中的公交换乘问题可能是一个典型的旅行商问题(Traveling Salesman Problem, TSP)变种,或者是寻找最优路径的问题,其中涉及到公交网络中不同站点之间的最短路径或最少换乘次数。解决此类问题通常需要使用动态规划、贪心算法或者一些启发式搜索算法,比如A*搜索或Dijkstra算法。 如果你需要了解题目的基本思路,可能会这样操作: 1. 建立一个图,节点代表公交站点,边代表两个站点之间的路线及其长度或换乘次数。 2. 对于每个节点,计算从起点到所有其他节点的最短路径,形成一个邻接矩阵或邻接表。 3. 使用动态规划方法,例如记忆化搜索,尝试所有可能的路径,每次选择当前未访问节点中距离最近的一个,直到遍历完所有节点并回到起点,记录下总的距离或换乘次数。 4. 为了优化,可以考虑使用启发式搜索策略,如用估算的总距离作为启发信息,优先探索看起来更优的路径。 如果你对具体解法有疑问,或者想了解某个步骤的详细操作,请告诉我,我会尽力解释。至于详细的题解,建议你查阅相关的代码库、论坛帖子或在线教程,它们通常会有文字描述和步骤示例。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值