UVA10160
2014-08-15 14:58
253 查看
题目的意思就是在图中建尽量少的服务站,使任一点都能在自己,或是相邻的点找到服务站。
一开始用普通的回溯搜,可以得出答案,但是超时了,剪了好多枝还是超时。所以去网上看了看题解,发现了一种用状态压缩的做法,觉得很神奇,就学着写了写。
状态压缩就是用二进制表示状态,好处就是可以用位运算操作,
像 st[i] 如果和 1 << 5 进行或运算,那么st[i] 第五个位置上就变成了1.,如5的二进制是0101 ,可以表示在这个点建站那么城市1 和 3是可以连到的,如果这时发现城市2也可以连到,那么 在和 1 << (2 - 1) 也就是0010进行一次或运算 ,这样结果就变成0111 。
第一步`输入完得到每个城市如果建站,会有哪些城市可以用,也就是相邻。
然后用回溯,如果只建一座,如果建2座 ,到可以实现时输出,就是最小的。
dfs第一个参数是当前的状态,也就是哪些城市符合要求(变成二进制时1的地方就是符合要求的地方),第二个参数是已经建了几座,第三个是要建几座,第四个参数是当前回溯要从哪个城市开始循环。
前两个if 都属于剪枝 ,第一个意思是如果当前状态,i 之后的城市全部建上,也没有办法达到全部符合,那就直接返回不行, 第二个if 表示加上这座城市,状态没有变化,那也没有递归的必要。然后是继续把状态加上,建的数量加一,递归
AC代码:
一开始用普通的回溯搜,可以得出答案,但是超时了,剪了好多枝还是超时。所以去网上看了看题解,发现了一种用状态压缩的做法,觉得很神奇,就学着写了写。
状态压缩就是用二进制表示状态,好处就是可以用位运算操作,
像 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;
}
相关文章推荐
- UVA - 10160 Servicing Stations 剪枝+回溯
- uva 10160服务站
- UVa 10160 - Servicing Stations
- UVA10160在一个图中选择几个点,使得这些点加上相邻的点为这个图的顶点集
- uva 10160 Servicing stations
- uva 10160 Servicing stations
- uva 10160 Servicing Stations(DFS+剪枝)
- uva 10160 Servicing Stations(DFS+剪枝)
- UVa Problem 10160 Servicing Stations (服务站)
- uva 10160(回溯)
- UVA 10160 Servicing Stations
- uva 10160
- uva 10160
- uva 10160 Servicing Stations
- UVA 10160 Servicing Stations
- uva10160(dfs+状态压缩)
- UVA 10160 - Servicing Stations
- Uva 307木棍、387谜题、10160服务站总结
- uva 10160 Servicing Stations
- UVA 10160 Servicing Stations(深搜 + 剪枝)