一
试题 算法训练 审美课
资源限制
时间限制: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;
}