题目
题目描述
你一个程序员,不知为何就当上了你们镇的镇长(人的一生当然要靠自我奋斗,当然也要考虑历史的进程)。你们镇有 N (3 <= N <= 35)个村,分别标号为 1, 2, …, N,有些村常年供电不足。现在你需要重新规划镇上的供电站的选址。现在的要求是,对于镇里的每个村,要么这个村有个供电站,要么这个村相邻的村中有一个供电站。你最少需要建几个供电站?
输入描述
有多组输入。
对于每组测试数据,第一行两个数 N, M,分别表示村子数量和直接相连的村子关系的数量。
接下来 M 行,每行两个数,表示这两个村子直接相连。
注意: 最后一组测试数据为 N = 0, M = 0,是一个标记,表示输入结束,无需计算和输出任何东西。
输出描述
每组测试数据输出一个整数,表示最少需要建的供电站数量。
样例输入
8 12
1 2
1 6
1 8
2 3
2 6
3 4
3 5
4 5
4 7
5 6
6 7
6 8
0 0
样例输出
2
题目分析
本题初看是一个暴力,暴力枚举选哪些村庄修建发电站然后搜索一遍图看一看是不是全部符合要求。但是想一想也知道,这样肯定会超时,而且如果用二进制来枚举的话,没法控制先枚举小数量再枚举大数量。因此,本题采用迭代加深搜索。
关于迭代加深搜索,我以前的文章有出现过:
【算法竞赛入门经典】7.6 迭代加深搜索与IDA* 例题7-10 UVa11211
这里将其利用到确定修建几个电站上面,最大深度是几就是最大能修几个电站。从小的往大去搜索。
另外,为了快速运算,本题使用一维数组表示每个村庄,每个村庄中第i位若是为1则表示与第i个村庄相邻,否则就是不相邻。这样,题目又可以简化成,挑选出最小个数的村庄,使其并起来每一位都是1。
搜索的格式为dfs(本次迭代优先搜索的最大层数,当前层数,已选村庄中编号最靠后的那个村庄的标号,long long a表示的每个村子点亮情况)
下面考虑剪枝,目前有二种剪枝策略:
1.若是新选出了第i个村庄,产生的效果与原来情况取并相等的话,实际上就是没有作用,肯定不是最优解,直接放弃本次搜索
2.这里也解释了为什么dfs()中有已选村庄中编号最靠后的那个村庄的标号的原因了。当规定从第一个村庄开始往后选择的时候,不允许回头选择。因此若是选到第i个村庄之后,后面i+1~n个村庄全部选择都不能使得所有村庄点亮,则后面不可能满足题意了,因此放弃本次搜索。所以要求有序的从前往后搜索(从后往前也行,只不过后面的要改),第二个要求是求出第i个村庄到第n个村庄都点亮的话,产生的效果。
本题的坑
因为采用二进制表示某个村庄的相邻情况或是点亮情况,所以二进制的使用会有坑
即使
long long a;
a = 1<<32;
这样也会出问题,因为1是int,1<<32对于int来说变成一个负数了,然后这个负数复制给a那么a不可能是你想要的结果,因此,正确的做法应该是:
long long a;
long long one = 1;
a = one<<32;
//a = (long long)1 <<32;
//a = 1ll <<32;
以上写法都可以,本质上是要求1先是long long然后再进行移位
整体代码与运行结果
/*
ZhangBinjie@Penguin
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 36
using namespace std;
long long dat[maxn];
long long tailsum[maxn];
long long dest;
long long one = 1;
int n, m;
bool dfs(int depth, int step, int ci, long long ans) {
if (ans == dest)
return true;
if (step == depth)
return false;
for (int i = ci; i <= n; ++i) {
if ((ans | tailsum[i]) != dest)
break;
if ((ans | dat[i]) == ans)
continue;
if (dfs(depth, step + 1, i + 1, ans | dat[i]))
return true;
}
return false;
}
int main() {
while (cin >> n >> m && n) {
int a, b;
memset(dat, 0, sizeof(dat));
memset(tailsum, 0, sizeof(dat));
dest = (one << n) - 1;
for (int i = 0; i < m; ++i) {
cin >> a >> b;
dat[a] |= one << (b - 1);
dat[b] |= one << (a - 1);
}
for (int i = 1; i <= n; ++i)
dat[i] |= one << (i - 1);
tailsum[n] = dat[n];
for (int i = n - 1; i >= 1; --i) {
tailsum[i] = dat[i];
tailsum[i] |= tailsum[i + 1];
}
for (int i = 1; i <= n; ++i) {
if (dfs(i, 0, 1, 0)) {
cout << i << endl;
break;
}
}
}
return 0;
}
结果如下