每日一燴(狀態壓縮,哈希)

試題 算法訓練 審美課
資源限制
時間限制: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章