一
試題 算法訓練 審美課
資源限制
時間限制: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;
}