UVA 818 Cutting Chains(状压 + 暴搜)题解
题意:有1~n个小环,他们中的有些互相扣在一起,问你至少切开几个能把这写小环串成一条链
思路:还是太菜了,题目给的n<=15,显然可以暴力解决。
用二进制表示每个环切还是不切,然后搜索所有情况。当一种情况满足一下两点:1.切完之后每一串连在一起的环应该是一条链,没有分支没有环;2.当一个环被切开,那么他就可以当做任意串的连接点,那么显然切开的点+1>=串的数量。
代码:
#include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; using namespace std; const int maxn = 15 + 10; const int seed = 131; const ll MOD = 100000007; const int INF = 0x3f3f3f3f; int vis[maxn], del[maxn], mp[maxn][maxn]; int n, ans, u, v, ca = 1, flag; bool branch(){ int deg = 0; for(int i = 1; i <= n; i++){ deg = 0; if(!del[i]){ for(int j = 1; j <= n; j++){ if(mp[i][j] && !del[j]) deg++; } } if(deg > 2) return false; } return true; } bool dfs(int x, int pre){ bool yes = true; vis[x] = 1; for(int i = 1; i <= n; i++){ if(mp[x][i] && i != pre && !del[i]){ if(vis[i]) return false; yes = dfs(i, x); if(!yes) return false; } } return true; } bool loop(int &rest){ memset(vis, 0, sizeof(vis)); flag = 0; for(int i = 1; i <= n; i++){ if(!vis[i] && !del[i]){ if(!dfs(i, -1)) return false; rest++; } } return true; } int main(){ while(scanf("%d", &n) && n){ memset(mp, 0, sizeof(mp)); while(scanf("%d%d", &u, &v) && u != -1 && v != -1){ mp[u][v] = mp[v][u] = 1; } int ans = INF, delet, rest; for(int i = 0; i < (1 << n); i++){ memset(del, 0, sizeof(del)); delet = rest = 0; for(int j = 0; j < n; j++){ if(i & (1 << j)){ del[j + 1] = 1; delet++; } } if(!branch() || !loop(rest)) continue; //printf("%d\n", delet); if(delet + 1 >= rest){ ans = min(ans, delet); } } printf("Set %d: Minimum links to open is %d\n", ca++, ans); } return 0; }