题目链接:最后的舞会 - 题目 - Daimayuan Online Judge
题目描述:
老师为即将毕业的同学们准备了一场舞会,有2N个同学会参加这场舞会,他们会被分成N对跳舞,每个人有一个编号,如果编号为i的同学和编号为j的同学配对,那么这一对的满意度是Ai,j(i<j),我们规定这个舞会的满意度为每一队的满意度的异或和,也就是说,当同学们分成N组后,第i对同学的满意度为Ai,那么舞会的满意度为A1⊕A2⊕...AN
请你求出本场舞会满意度的最大值
输入描述:
第一行给出一个数N,有2N个人参加舞会
接下来给出一个矩阵表示i和j配对时的满意度
A1,2,A1,3,..A1,2N
A2,3,...A2,2N
.. .. ..
A2N−1,2N
其中1≤N≤8,0≤Ai,j≤2^30
输出描述:
输出本场舞会满意度的最大值
样例:
输入:
2
4 0 1
5 3
2
输出:
6
可以看到n的范围很小,由n的范围可以很容易想到用dfs搜索,个人认为此题与八皇后问题有些类似,也是考虑每一行每一列都只能选一个数。考虑搜索每一对,一对一对去匹配,而此题如果爆搜容易超时所以需要进行剪枝。剪枝的基本思想对于此题是减少重复,而因为此题每个数都必须被选到,所以可以在搜索由i递增依次去搜。对1可以直接加入,因为1肯定会被匹配,再去找与1匹配的位置(小剪枝)。因为每一个数都必须被匹配到,所以在对每一对的匹配搜索中,找到第一个未被匹配到的数就可以对其进行匹配,进而枚举与它匹配的位置。
这里还有一个小的注意点,因为aij的范围<=2^30,不会爆int,可以直接用int就行,实测开longlong比int慢将近两倍多
代码:
#include <bits/stdc++.h>
using namespace std;
bool vis[20];
int a[20][20];
int ans, n;
void dfs(int step, int res) // step为当前已经匹配的对数,res是当前的异或和
{
if (step == n) //已全部匹配更新答案
{
ans = max(ans, res);
return;
}
int i;
for (i = 2; i <= n + n - 1; i++)
{
if (!vis[i]) //找到1~2n中第一个未被匹配的数(剪枝)
break;
}
for (int j = i + 1; j <= n + n; j++)
{
if (vis[j] == 0) //未被匹配
{
vis[i] = 1, vis[j] = 1; //匹配
dfs(step + 1, res ^ a[i][j]);
vis[i] = 0, vis[j] = 0; //取消标记
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n + n - 1; i++)
for (int j = i + 1; j <= n + n; j++)
cin >> a[i][j];
vis[1] = 1; // 1肯定会被匹配,直接加入匹配(剪枝)
for (int i = 2; i <= n + n; i++) //枚举与1匹配的位置
{
vis[i] = 1;
dfs(1, a[1][i]);
vis[i] = 0;
}
cout << ans;
}
八皇后问题
题目链接:八皇后问题 - 题目 - Daimayuan Online Judge
题目描述:
在一个n×n的棋盘中,放入n个皇后,要求皇后之间不能互相攻击,输出所有的方案。
一个皇后,能攻击同一行,同一列,同一对角线的的所有格子。
输入格式:
第一行输入一个整数n。
输出格式:
输出若干行,每行输出n个整数,依次表示第1,2,…,n行的皇后的所在的列。
按字典序从小到大输出。
n个皇后不能在同一行或同一列或同一对角线,也即对每一行每一列每一个对角线都只能有一个皇后。考虑枚举每一行皇后占用的列。
对点(x,y),它的主对角线的下一个位置为(x+1,y+1)上一个位置为(x-1,y-1),满足行标减列标的值相等,以此去标记是否在同一主对角线。因为数组下标不能为负数所以对每个行标减列标的值加上n。
同理,对点(x,y),它的副对角线的下一个位置为(x+1,y-1)上一个位置为(x-1,y+1),满足行标加上列标的值相等,以此去标记是否在同一副对角线。
代码:
#include <bits/stdc++.h>
using namespace std;
int n, dis[13], s[13], dx[30], dy[30]; // s[i]为第i行s[j]处放入皇后
// dis标记每一列是否被占用,s存皇后的位置,dx标记副对角线,dy标记主对角线
void dfs(int step) // step为当前已放入的皇后数量
{
if (step == n) //全部放入输出答案
{
for (int i = 1; i <= n; i++)
printf("%d ", s[i]);
printf("\n");
return;
}
for (int i = 1; i <= n; i++)
{
if (dis[i] == 0 && dx[step + 1 + i] == 0 && dy[step + 1 - i + n] == 0)
{
s[step + 1] = i; //第step+1行第i列放入皇后
dis[i] = 1; //标记第i列已经被占用
dx[step + 1 + i] = 1; //标记副对角线
dy[step + 1 - i + n] = 1; //标记主对角线
dfs(step + 1);
dis[i] = 0; //取消标记
dx[step + 1 + i] = 0;
dy[step + 1 - i + n] = 0;
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) //字典序从小到大输出所以i从小到大进行枚举
{
s[1] = i; //第1行第i列放入皇后
dis[i] = 1; //标记第i列已经被占用
dx[1 + i] = 1; //标记副对角线
dy[1 - i + n] = 1; //标记主对角线,因为i-j可能为负数故而+n来处理
//(不能取abs!因为abs(1-2)=1且abs(2-1)=1而(1,2),(2,1)并不在同一主对角线
dfs(1);
dis[i] = 0; //取消标记
dx[1 + i] = 0;
dy[1 - i + n] = 0;
}
}
ending......
鄙人不才,如有问题欢迎指正!鄙人不胜感激!