剪邮票
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
思路:全排列 + 连通性判断,我用的bfs判断的连通性
注意:由于重复计数最后的结果需要除以5!
#include <bits/stdc++.h>
using namespace std;
int vis[20]; // dfs
vector<int> vv;
int vit[20]; //bfs
queue<int> qq;
int a[20][20];
long long ans = 0;
void init() {
a[1][2] = 1, a[1][5] = 1;
a[2][1] = 1, a[2][3] = 1, a[2][6] = 1;
a[3][2] = 1, a[3][4] = 1, a[3][7] = 1;
a[4][3] = 1, a[4][8] = 1;
a[5][1] = 1, a[5][6] = 1, a[5][9] = 1;
a[6][2] = 1, a[6][5] = 1, a[6][7] = 1, a[6][10] = 1;
a[7][3] = 1, a[7][6] = 1, a[7][8] = 1, a[7][11] = 1;
a[8][4] = 1, a[8][7] = 1, a[8][12] = 1;
a[9][5] = 1, a[9][10] = 1;
a[10][6] = 1, a[10][9] = 1, a[10][11] = 1;
a[11][7] = 1, a[11][10] = 1, a[11][12] = 1;
a[12][8] = 1, a[12][11] = 1;
}
bool in(int x) {
for (int i = 0; i < vv.size(); i++) {
if (vv[i] == x) {
return true;
}
}
return false;
}
void bfs(int x) {
// reset
while (!qq.empty()) {
qq.pop();
}
for (int i = 1; i <= 12; i++) {
vit[i] = 0;
}
qq.push(x);
vit[x] = 1;
while (!qq.empty()) {
int t = qq.front();
qq.pop();
for (int i = 1; i <= 12; i++) {
if (!vit[i] && a[t][i] == 1 && in(i)) {
qq.push(i);
vit[i] = 1;
}
}
}
}
void dfs(int x) {
if (x == 5) {
// for (int i = 0; i < 5; i++) {
// cout << vv[i] << " ";
// }
// cout << endl;
int temp = vv[0];
bfs(temp);
bool flag = true;
for (int i = 0; i < 5; i++) {
if (!vit[vv[i]]) {
flag = false;
break;
}
}
if (flag) {
ans++;
// for (int i = 0; i < 5; i++) {
// cout << vv[i] << " ";
// }
// cout << endl;
}
return ;
}
for (int i = 1; i <= 12; i++) {
if (!vis[i]) {
vv.push_back(i);
vis[i] = 1;
dfs(x + 1);
vis[i] = 0;
vv.pop_back();
}
}
}
int main() {
init();
dfs(0);
cout << ans / (5*4*3*2*1);
return 0;
}