每日一烩(状态压缩,哈希)

试题 算法训练 审美课
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
  《审美的历程》课上有n位学生,帅老师展示了m幅画,其中有些是梵高的作品,另外的都出自五岁小朋友之手。老师请同学们分辨哪些画的作者是梵高,但是老师自己并没有答案,因为这些画看上去都像是小朋友画的……老师只想知道,有多少对同学给出的答案完全相反,这样他就可以用这个数据去揭穿披着皇帝新衣的抽象艺术了(支持帅老师_)。
  答案完全相反是指对每一幅画的判断都相反。
输入格式
  第一行两个数n和m,表示学生数和图画数;
  接下来是一个n*m的01矩阵A:
  如果aij=0,表示学生i觉得第j幅画是小朋友画的;
  如果aij=1,表示学生i觉得第j幅画是梵高画的。
输出格式
  输出一个数ans:表示有多少对同学的答案完全相反。
样例输入
3 2
1 0
0 1
1 0
样例输出
2
样例说明
  同学1和同学2的答案完全相反;
  同学2和同学3的答案完全相反;
  所以答案是2。
数据规模和约定
  对于50%的数据:n<=1000;
  对于80%的数据:n<=10000;
  对于100%的数据:n<=50000,m<=20。
  
巧用了num数组和状态压缩,只需要循环一次,不然两层循环最后一个测试会超时

#include<bits/stdc++.h>
using namespace std;

int mat[50000];
int num[1<<20];

int main() {
	memset(num, 0, sizeof(num));
	memset(mat, 0, sizeof(mat));
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; i++) {
		int tmp;
		for (int j = 0; j < m; j++) {
			scanf("%d", &tmp);
			mat[i] = (mat[i] << 1) + tmp;
		}
		num[mat[i]]++;
	}



	int INF = (1 << m) - 1;
	int ans = 0;
	for (int i = 0; i < n; i++) {	
		ans += num[mat[i]^INF];	
	}
	
	//int INF = (1 << m) - 1;		//这样会超时
	//int ans = 0;
	//for (int i = 0; i < n; i++)
		//for (int j = i + 1; j < n; j++)
			//if ((mat[i] ^ mat[j] )== INF)
				//ans++;
				
	printf("%d", ans/2);
	return 0;
}

问题描述
在九宫格里放在1到8共8个数字还有一个是空格,与空格相邻的数字可以移动到空格的位置,问给定的状态最少需要几步能到达目标状态(用0表示空格):
1 2 3
4 5 6
7 8 0

输入
输入一个给定的状态。

输出
输出到达目标状态的最小步数。不能到达时输出-1。

输入样例
1 2 3
4 0 6
7 5 8

输出样例
2

掌握Hash映射

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const int N = 1000000, HN = 1000003;
int head[HN], next[N];//链表(用于哈希)
int st[N][9], goal[9];
int dis[N];
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
 
int Hash(int *st) {
	int v = 0;
	for(int i = 0; i < 9; i++)
		v = v*10 + st[i];//恰如其分得将9个数字映射成9位数
	return v % HN;//确保hash值不超过hash表大小
}
 
bool try_insert(int rear) {
	int h = Hash(st[rear]);
	int u = head[h];
	while(u) {
		if(!memcmp(st[u], st[rear], sizeof(st[0])))
			return 0;//重复,不挂,返回假
		u = next[u];
	}
	next[rear] = head[h];//rear指向旧的head[h]
	head[h] = rear;//rear成为新的head[h],如此一来,就把rear插到链表的头上了
	return 1;
}
 
int bfs() {
	memset(head, 0, sizeof(head));//初始化查找表,其实就是表头们
	int fron = 1, rear = 2;
	while (fron < rear) {
		if (!memcmp(goal, st[fron], sizeof(st[0])))
		    return fron;//找到目标图
		int z;
		for(z = 0; z < 9; z++)
		if(!st[fron][z])//找到白格
			break;//更新z为队首的白格
		int x = z / 3, y = z % 3;
		for(int d = 0; d < 4; d++) {
			int nx = x + dx[d], ny = y + dy[d], nz = 3*nx + ny;
			if(nx >= 0&&nx < 3&&ny >= 0&&ny < 3) {//判断边界
				memcpy(&st[rear], &st[fron], sizeof(st[0]));
				st[rear][nz] = st[fron][z];
				st[rear][z] = st[fron][nz];//这是一次移动的尝试
				dis[rear] = dis[fron] + 1;
				if(try_insert(rear))//判重,若不重,则进队
					rear++;
			}
		}
		fron++;//完成队首的尝试,队首出队。这个bfs和普通的bfs不太一样,st[][]其实就是队列,就是很多张图
	}
	return 0;
}
 
int main() {
	freopen("test.in", "r", stdin);
	for(int i = 0; i < 9; i++)
		scanf("%d", &st[1][i]);
	for(int i = 0; i < 9; i++)
		scanf("%d", &goal[i]);
	int ans = bfs();
	if(ans > 0)
		printf("%d\n", dis[ans]);
	else
		puts("-1");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值