您的位置:首页 > 其它

UVA10160

2014-08-15 14:58 253 查看
题目的意思就是在图中建尽量少的服务站,使任一点都能在自己,或是相邻的点找到服务站。

一开始用普通的回溯搜,可以得出答案,但是超时了,剪了好多枝还是超时。所以去网上看了看题解,发现了一种用状态压缩的做法,觉得很神奇,就学着写了写。

状态压缩就是用二进制表示状态,好处就是可以用位运算操作,

像 st[i] 如果和 1 << 5 进行或运算,那么st[i] 第五个位置上就变成了1.,如5的二进制是0101 ,可以表示在这个点建站那么城市1 和 3是可以连到的,如果这时发现城市2也可以连到,那么 在和 1 << (2 - 1) 也就是0010进行一次或运算 ,这样结果就变成0111 。

第一步`输入完得到每个城市如果建站,会有哪些城市可以用,也就是相邻。

while (cas--) {
cin >> a >> b;
st[a] |= ((ll)1 << (b - 1));
st[b] |= ((ll)1 << (a - 1));
}


然后用回溯,如果只建一座,如果建2座 ,到可以实现时输出,就是最小的。

for (int i = 1 ; i <= num ;i++) {
if (dfs((ll)0,0,i,1)) {
cout << i <<endl;
break;
}
}


dfs第一个参数是当前的状态,也就是哪些城市符合要求(变成二进制时1的地方就是符合要求的地方),第二个参数是已经建了几座,第三个是要建几座,第四个参数是当前回溯要从哪个城市开始循环。

for (int i = k ; i <= num ;i++){
if( (sta | vis[i]) != ((ll)1 << num) - 1)
return false;
if( (sta | st[i]) == sta)
continue;
if( dfs ( (sta | st[i]) , now + 1 ,target , k + 1))
return true;
}


前两个if 都属于剪枝 ,第一个意思是如果当前状态,i 之后的城市全部建上,也没有办法达到全部符合,那就直接返回不行, 第二个if 表示加上这座城市,状态没有变化,那也没有递归的必要。然后是继续把状态加上,建的数量加一,递归

AC代码:

#include<iostream>
using namespace std;
#define ll long long
const int N = 35 + 5;
ll st
;
ll vis
;
int num,cas;
bool dfs(ll sta ,int now ,int target , int k ) {
if (now == target) {
if(sta == ((ll)1 << num) - 1)
return true;
else
return false;
}
for (int i = k ; i <= num ;i++){ if( (sta | vis[i]) != ((ll)1 << num) - 1) return false; if( (sta | st[i]) == sta) continue; if( dfs ( (sta | st[i]) , now + 1 ,target , k + 1)) return true; }
return false;
}
int main () {
int a,b;
while (cin >> num >> cas && num + cas) {
for (int i = 0 ; i < N ;i++) {
vis[i] = 0;
}
for (int i = 0 ; i < num ; i++) {
st[i + 1] = ((ll)1 << i);
}
while (cas--) { cin >> a >> b; st[a] |= ((ll)1 << (b - 1)); st[b] |= ((ll)1 << (a - 1)); }
vis[num] = st[num];
for (int i = num - 1; i > 0 ;i--) {
vis[i] = vis[i + 1]|st[i];
}
for (int i = 1 ; i <= num ;i++) { if (dfs((ll)0,0,i,1)) { cout << i <<endl; break; } }
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: