数据结构哈希题目

购物券(哈希+dfs的妙用)

Description
小Y得到了两张价值不菲的SHOP购物券,所以他决定去买N件礼物送给朋友们。小Y选好了n件礼物,并且它们的价格之和恰好为两张购物券的面值之和。当小Y被自己的聪明所折服,高兴地去结账时,他突然发现SHOP对购物券的使用有非常奸诈的规定:一次只允许使用一张、不找零、不与现金混用。小Y身上根本没有现金,并且他不愿意放弃挑选好的礼物。这就意味着,他只能通过这两张购物券结账,而且每一张购物券所购买的物品的总价格必须精确地等于这张购物券的面额。怎样才能顺利地买回这n件礼物呢?你的任务就是帮助小Y确实是否存在一个购买方案。小Y会告诉你其中一张购物券的面额以及所有商品的价格,你只需要确定能否找到一种方案使得选出来的物品的价格总和正好是这张购物券的面额即可。
Input
输入有多组数据,每两行有一组数据。每组数据的第一行为两个整数n和m,分别表示小Y一共挑选了n个物品以及小Y的一张购物券的面额为m,接下来的一行有n个用空格隔开的正整数,第i个数表示第i物品的价格。
Output
输出包含若干行,每行一个单词"YES"或者"NO",分别代表存在一个购买方案和不存在一个购买方案。
Samples
Input
10 2000
1000 100 200 300 400 500 700 600 900 800
10 2290
1000 100 200 300 400 500 700 600 900 800
Output
YES
NO
Hint
对于30%的输入文件,所有的n≤20;
对于100%的输入文件,所有的n≤40,并且m和物品的总价值不超过231−1,测试组数不超过10组,不少于5组。
Source
高级数据结构 1 哈希表

思路:他问有没有若干个物品的价值之和正好等于购物券的面额。
咱们朴素的想法就是dfs枚举所有状态,每个物品可以选可以不选这样一来就是240个状态,妥妥T飞。因此我们吧所有商品分两段,分别进行dfs,我们先dfs前半段,判断是否有组合满足条件,同时我们把各种物品组合的价值映射一下,在dfs后半段的时候会用到,这里我们用链地址法来哈希,,如果不存在,那咱们继续dfs后半段。我们借用哈希表就完美完成了该工作。

注:第二遍dfs的要及时return,要不会超时


Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PI;
const int N = 2e4+10;
const int mod = 1e5;
int n,m;
int a[55];
vector<int> v[mod+10];
bool f;
void dfs(int now,int s) {
	if(s == m) {
		f=1;
		return ;
	}
	int ip = s%mod;
	v[ip].push_back(s);
	if(now==n/2) return;
	if(s + a[now+1] <= m) dfs(now+1,s+a[now+1]);
	dfs(now+1,s);
}
int dfs_(int now,int s) {
	if(now>n) return 0;
	int ip = (m-s) % mod;
	for(int i=0; i<v[ip].size(); i++) if(s+v[ip][i]==m) return 1;
	bool x = 0;
	if(s + a[now+1] <= m)  {
		x = dfs_(now+1,s+a[now+1]);
		if(x) return 1;
	}
	x = dfs_(now+1,s);
	return x;
}
int main() {
	while(~scanf("%d%d",&n,&m)) {
		f = 0;
		for(int i=1; i<=n; i++) scanf("%d",&a[i]);
		for(int i=0; i<=mod+1; i++) v[i].clear();

		dfs(0,0);
		if(f) {
			puts("YES");
			continue;
		}

		dfs_(n/2,0)==1?puts("YES") : puts("NO");
	}

	return 0;
}

魔板 (哈希+bfs)

Description
在魔方风靡全球之后不久,Rubik先生发明了它的简化版——魔板。魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示。任一时刻魔板的状态可用方块的颜色序列表示:从魔板的左上角开始,按顺时针方向依次写下各方块的颜色代号,所得到的数字序列即可表示此时魔板的状态。例如,序列(1,2,3,4,5,6,7,8)表示魔板状态为:
在这里插入图片描述
对于魔板,可施加三种不同的操作,具体操作方法如下:
A: 上下两行互换
B: 每行同时循环右移一格
C: 中间4个方块顺时针旋转一格
应用这三种基本操作,可以由任一状态达到任意另一状态。
在这里插入图片描述
上图描述了上述三种 操作的具体含义,图中方格外面的数字标识魔板的8个方块位置,方格内的数字表示此次操作前该小方块所在位置,即:如果位置P对应的方格中数字为I,则表示此次操作前该方块在位置I。
任务一:请编写程序,对于输入的一个状态寻找一种操作的序列,使得从初始状态开始,经过此操作序列后该魔板变为目标状态。
任务二:如果你的程序寻找到的操作序列在300步以内,会得到任务二的分数。
Input
只有一行,内容是8个以一个空格分隔的正整数,表示目标状态。输入样例对应的状态如下图所示。
在这里插入图片描述
Output
第一行输出程序寻找到的操作序列的步数L,随后L行是相应的操作序列,每行的行首输出一个字母,表示相应的操作。
Samples
Input
2 6 8 4 5 7 3 1
Output
7
B
C
A
B
C
C
B
Source
高级数据结构 1 哈希表

思路:第一想法就是bfs这个不难想,也没啥坑点,直接写就OK了


Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PI;
const int N = 2e4+10;
const int mod = 100003;
struct Node {
	string s,root;
	int step;
};
string en,ans;
map<string,bool> mp;
int bfs() {
	queue<Node> Q;
	Node st;
	st.step = 0;
	st.s = "12345678";
	st.root = "";
	mp[st.s] = 1;
	Q.push(st);
	while(Q.size()) {
		Node tep = Q.front();
		Q.pop();
		string now = tep.s;
		if(now == en) {
			ans = tep.root;
			return tep.step;
		}
		Node t1,t2,t3;
		t1.step = t2.step = t3.step = tep.step + 1;
		t1.root = tep.root + "A";
		t2.root = tep.root + "B";
		t3.root = tep.root + "C";
		t1.s = now;
		reverse(t1.s.begin(),t1.s.end());
		t2.s = now[3] + now + now[4];
		t2.s.erase(t2.s.begin()+4,t2.s.begin()+6);
		t3.s = now;
		t3.s.insert(1,1,now[6]);
		t3.s.insert(6,1,now[2]);
		t3.s.erase(t3.s.begin()+3);
		t3.s.erase(t3.s.begin()+7);
		//cout<<t1.s<<" "<<t2.s<<" "<<t3.s<<endl;
		if(!mp[t1.s]) mp[t1.s] = 1,Q.push(t1);
		if(!mp[t2.s]) mp[t2.s] = 1,Q.push(t2);
		if(!mp[t3.s]) mp[t3.s] = 1,Q.push(t3);
	}
}
int main() {
	en.clear();
	for(int i=1; i<=8; i++) {
		int x;
		cin >> x;
		en += '0' + x;
	}
	cout<<bfs()<<endl;
	for(int i=0; i<ans.size(); i++) {
		if(i) putchar('\n');
		cout<<ans[i];
	}
	return 0;
}

Rush Rush!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值