2.5C语言学习

本文介绍了四道编程竞赛题目,涉及动态规划、深度优先搜索(BFS)、哈希技术在迷宫问题中的应用,以及字符串哈希和无错哈希的解决方案。展示了如何在实际问题中运用这些技术寻找最优解或解决特殊条件下的搜索问题。
摘要由CSDN通过智能技术生成

P1473 [USACO2.3] 零的数列 Zero Sum

第一眼看到这道题想到的是动态规划,可能是最近一直在看这种题,但是我也不会怎么写,看到题解区的dfs我才想到可以搜索,可是要怎么搜呢,这是一个棘手的问题,题目还有进位操作,但是拆分下来也就只有三个操作,加减拆,所以不妨以这三种状态遍历搜索

#include<bits/stdc++.h>
using namespace std;
int n,a[11];
char s[5]=" +-";
bool check(){
	int ans=0,t;
	for(int i=1;i<=n;i++){
		if(a[i]==0)continue;
		t=i;
		for(int j=i+1;j<=n;j++){
			if(a[j]!=0)break;
			t=t*10+j;
		}
		if(a[i]==1)ans+=t;
		else ans-=t;
	}
	if(ans==0)return true;
	else return false;
}
void dfs(int k){
	if(k==n+1){
		if(check()){
			printf("1");
			for(int i=2;i<=n;i++)printf("%c%d",s[a[i]],i);
			printf("\n");
		}
	}
	else{
		for(int i=0;i<=2;i++){
			a[k]=i;
			dfs(k+1);
		//	a[k]=2;
		}
	}
}
int main(){
	scanf("%d",&n);
	a[1]=1;
	dfs(2);
	return 0;
}

P8673 [蓝桥杯 2018 国 C] 迷宫与陷阱

求最短路径无疑是用bfs,但是这题有个无敌道具,有了无敌道具可以无视陷阱

在结构体内新加一个变量 magic,表示当前还剩几步的无敌效果。在遍历过程中,如果遇到“X”,并且不剩无敌了,就不能走;如果遇到“%”,magic 变为 k。其他情况,并且不为“#”时,把点入队。

这里要加一个剪枝:加一个 int 型的数组vis,入队时需要 vis[x][y] < magic,即如果当前节点已经被访问过,且之前到达该节点时的无敌状态剩余步数比现在要多,则不需要再次访问该节点。因为如果我们之前已经访问过该节点并且使用了更多的无敌步数,那么这条路径一定不是最优解。初始时要赋值为全 −1−1。

这是题解的处理无敌时间的方法,我觉得可以记忆一下,因为以前从没有用过也不会用,好的方法确实是精妙

#include<bits/stdc++.h>
using namespace std;
struct node{
	int x,y,step,magic;
};
int vis[1009][1009];
char maps[1009][1009];
int dx[5]={0,0,1,-1},dy[5]={1,-1,0,0},n,k;
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>maps[i][j];
		}
	}
	queue<node>que;
	memset(vis,-1,sizeof(vis));
	vis[1][1]=0;
	que.push((node){1,1,0,0});
	while(!que.empty()){
		node a=que.front();
		que.pop();
		if(a.x==n&&a.y==n){
			printf("%d",a.step);
			return 0;
		}
		for(int i=0;i<4;i++){
			int tx=a.x+dx[i],ty=a.y+dy[i];
			if(maps[tx][ty]=='X'&&a.magic==0)continue;
			int magic=max(a.magic-1,0);
			if(maps[tx][ty]=='%')magic=k;
			if(tx>=1&&ty>=1&&tx<=n&&ty<=n&&vis[tx][ty]<magic&&maps[tx][ty]!='#'){
				vis[tx][ty]=magic;
				que.push((node){tx,ty,a.step+1,magic});
			}
		}
	}
	printf("-1");
	return 0;
}

P8630 [蓝桥杯 2015 国 B] 密文搜索

我觉得这道题的难点应该是在于对题意的理解,测试的时候看了好几遍也没有看懂,看了题解才知道这题考的是什么东西,用map直接就可以秒了,不过正解是哈希的做法,可惜我还不会,只能用stl容器了。我们看到给出的 n 个小串可能是被打乱了再成为大串的子串的,所以我们可以先把给定的 n 个串每个串都按字符大小重新排序成一个按字典序排好的小串,然后考虑在枚举的时候把大串取出八个字符排序一下,然后再与上面排完序的 n 个小串比较是否相等即可。

对于这个比较是否相同的过程完全可以直接判或者用 map 来实现

#include<bits/stdc++.h>
using namespace std;
int main(){
	string str1,str2;
	int n;
	map<string,int>maps;
	cin>>str1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		cin>>str2;
		sort(str2.begin(),str2.end());
		maps[str2]++;
	}
	long long cnt=0;
	for(int i=0;i<str1.size()-7;i++){
		str2=str1.substr(i,8);
		sort(str2.begin(),str2.end());
		cnt+=maps[str2];
	}
	printf("%lld",cnt);
	return 0;
}

P2386 放苹果

本题的正解应该是动态规划,因为这种思维性的题一看就很有规律,奈何水平不够加上数据不大,那么好,我们直接开搜,反正N,M<=10,我们用dfs把所有情况遍历就完事了,每个盘能放0到当前剩余苹果值,但是由于不能有重复的情况,比如说0,0,1和0,1,0是一种,这时就要在dfs的参数里面放一个当前值每次从当前值开始搜索,这样保持一个单调递增的数量就能保证不重复

#include<bits/stdc++.h>
using namespace std;
int k,cnt;
void dfs(int pos,int now,int pp){
	if(pos==k&&pp==0){
		cnt++;
		return;
	}
	if(pos==k)return;
	for(int i=now;i<=pp;i++){
		dfs(pos+1,i,pp-i);
	}
}
int main(){
	int n;
	scanf("%d",&n);
	int m;
	for(int i=0;i<n;i++){
		cnt=0;
		scanf("%d %d",&m,&k);
		dfs(0,0,m);
		printf("%d\n",cnt);
	}
	return 0;
}

P3370 【模板】字符串哈希

这道题可以用stl容器的map或set直接过,不过既然是哈希的模板题,还是来了解一下吧

一、首先理解字符串操作的意义:

其实字符串操作的意义是很浅显的,比如百度的推荐搜索,所以字符串操作很重要啊

再来考虑时间:如果百度对于一个人的一次“常搜”推荐需要10s,那么对于全国网友来说,同时上网的人群基数很高,那么如果服务器性能不好的话,就会产生错误。

所以啊,打造高效的字符串算法是很有必要滴!

二、言归正传,浅析字符串哈希

哈希其实是所有字符串操作中,笔者认为最简单的操作了(except输入输出qwq)。哈希的过程,其实可以看作对一个串的单向加密过程,并且需要保证所加的密不能高概率重复(就像不能让隔壁老王轻易地用它家的钥匙打开你家门一样qwq),通过这种方式来替代一些很费时间的操作。

比如,最常见的,当然就是通过哈希数组来判断几个串是否相同(洛谷P3370)。此处的操作呢,很简单,就是对于每个串,我们通过一个固定的转换方式,将相同的串使其的“密”一定相同,不同的串 尽量 不同。

此处有人指出:那难道不能先比对字符串长度,然后比对ASCLL码之和吗?事实上显然是不行的(比如ab和ba,并不是同一个串,但是如是做却会让其认为是qwq)。这种情况就叫做hash冲突,并且在如此的单向加密哈希中,hash冲突的情况在所难免。

而我们此处介绍的,即是最常见的一种哈希:进制哈希。进制哈希的核心便是给出一个固定进制base,将一个串的每一个元素看做一个进制位上的数字,所以这个串就可以看做一个base进制的数,那么这个数就是这个串的哈希值;则我们通过比对每个串的的哈希值,即可判断两个串是否相同

#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
ll base=131;
ll a[10010];
char s[10010];
ll prime=234556;
ll mod=35897654678978910;
ll hashe(char s[]){
	int len=strlen(s);
	ll ans=0;
	for(int i=0;i<len;i++){
		ans=(ans*base+(ll)s[i])%mod+prime;
	}
	return ans;
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",s);
		a[i]=hashe(s);
	}
	sort(a+1,a+1+n);
	int cnt=1;
	for(int i=1;i<n;i++){
		if(a[i]!=a[i+1])cnt++;
	}
	printf("%d",cnt);
	return 0;
}

既然说了有map的做法我肯定也会试一下的,毕竟也算ac代码

#include<bits/stdc++.h>
using namespace std;
int main(){
	map<string,int>maps;
	int n;
	scanf("%d",&n);
	string str;
	long long cnt=0;
	for(int i=0;i<n;i++){
		cin>>str;
		maps[str]++;
		if(maps[str]==1)cnt++;
	}
	printf("%lld",cnt);
	return 0;
}

当然,再好的哈希也会有冲突,此时有两种做法可以解决或者降低哈希冲突的可能性

1、无错哈希

其实原理很简单,就是我们要记录每一个已经诞生的哈希值,然后对于每一个新的哈希值,我们都可以来判断是否和已有的哈希值冲突,如果冲突,那么可以将这个新的哈希值不断加上一个大质数,直到不再冲突(比如somebody’s birthday qwq)。

先贴代码:

for(int i=1;i<=m;i++)//m个串
{
cin>>str;//下一行的check为bool型 
while(check[hash(str)])hash[i]+=19260817;
hash[i]+= hash(str) ;
}

正如下图):

但是,这种方法类似桶查找,但是桶查找的弊端2就会很恶心——数据过大,check数组无能为力来支持上亿个空间(弊端1是由于数据具有跳跃性,浪费最后的统计次数,但在此不是特别明显,就当我皮了一下qwq)

2、多重哈希

这其实就是你用不同的两种或多种方式哈希,然后分别比对每一种哈希值是否相同——显然是增加了空间和时间,但也确实增加了其正确性。

下面皮一个哈希自动机;

//哈希自动机,需要二维hash数组 
for伪代码排序,用来使哈希值单调(更好判断相/不同的数量) 
for(int i=1;i<=m;i++){
	check=1; 
	for(int j=1;j<=qwq;j++)//皮一下
		if(hash[j][i]==hash[j][i+1]){check=0;break;} 
	if(check)ans++;//此为判断相同个数 
} 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值