UVa 818 Cutting Chains 题解

难度:β

建议用时:40 min

这题应该有迭代加深搜索的解法的,但我参考网友做法,用暴力枚举法。

大致思路是:枚举圆环的每种开闭状态,统计符合要求的最小的打开的圆环的数量。

要判断打开圆环的某一种方法是否符合要求,容易想到的一个是先判断除去这些已打开的圆环外的闭合圆环有没有组成一个环。

如果还有环,那么无论把打开的圆环怎样重新连城一条链套回去,都不能消除环,而题目要求我们够一条链。所以如果任然存在环的方法是不行的。

0000   0 (此时还有一个环没打开,不能组成链)

0    0

0000

当我们判断好剩下的环没有互相套成圈后,并不能充分说明这个方法就是符合要求的,就是可以统计开环数的。

0000 0000 0000 0

像上面那样,如果只有一个开环,三个链,因为只可以把单个环放回链的某个位置,而不能让两个链首尾相接(因为链的头尾没有打开),所以环太少而链太多的方法也是不行的。

现在可以充分说明这个方法有效了吧?(@@)

NO!

要知道如果原先的链十分错综复杂的连在一起,那么有可能当我们拆下某些环后,有这样的情况:

00‘0’00    0    0

0

0

注意打印号的环。在这种情况中,虽然没有环,也可以保证让整个环集连在一起且不会构成环,但是有一个环同时连着三个其他的环。这显然不是一条链。

0000000000000000

这才是链。

现在考虑了上述三种情况:1)没圈 2)多环少链 3)没“交际花”

这时候满足上述三种条件的拆环方法因该就可以被充分判断为有效的吧。可以统计了。

来看看具体实现吧。

这题因为只涉及到最多 15 个圆环,自然联想到用二进制表示圆环的开闭状态。

用 ”1” 对应开环,“0”表示闭环。于是枚举方法的 code:

1 for (int select = 0; select < (1<<n); select++) {
2         for (int i = 0; i < n; i++) {
3             if ((1<<i)&select) {
4                 cnt++;
5             }
6         }
7 }

这里顺带计数了。

这题核心算法不难,但很麻烦。

先上 code 吧。

 1 void mark_rings(int u, int fa) {
 2     mark[u] = 1;
 3     for (int i = 0; i < id[u]; i++) if(!cut[G[u][i]] && G[u][i] != fa) {
 4         mark_rings(G[u][i], u);
 5     }
 6 }
 7
 8 bool does_not_meet_condition(int number_of_cuts) {
 9     // exist a ring connect with more than two other rings
10     int cnt[maxn]; memset(cnt, 0, sizeof(cnt));
11     bool must_contains_circle = true;
12     for (int ring = 1; ring <= n; ring++) if (!cut[ring]) {
13         for (int i = 0; i < id[ring]; i++) {
14             if (!cut[G[ring][i]]) { cnt[ring]++; }
15         }
16         if (cnt[ring] > 2) return true;
17         if (cnt[ring] <= 1) must_contains_circle = false;
18     }
19     if (must_contains_circle) { return true; } // a chain without a ring connect with only one another ring is certainly a circle
20
21     int number_of_subchains = 0;
22     memset(mark, 0, sizeof(mark));
23     for (int ring = 1; ring <= n; ring++) if (!cut[ring] && !mark[ring] && cnt[ring] <= 1) {
24         // mark all rings connect with an ‘end‘
25         mark_rings(ring, 0);
26         number_of_subchains++;
27     }
28     for (int ring = 1; ring <= n; ring++) if (!cut[ring] && !mark[ring]) return true; // an unmarked ring implies a circle
29
30     // number of chains greater than number of cuts plus one
31     return number_of_subchains > number_of_cuts+1;
32 }
33
34 void solve() {
35     int ans = 20;
36     for (int select = 0; select < (1<<n); select++) {
37         int cnt = 0;
38         memset(cut, 0, sizeof(cut));
39         for (int i = 0; i < n; i++) {
40             if ((1<<i)&select) { // cut number (i+1)
41                 cut[i+1] = 1;
42                 cnt++;
43             }
44         }
45         if (does_not_meet_condition(cnt)) continue;
46         ans = min(ans, cnt);
47     }
48     cout << "Set " << ++kase << ": Minimum links to open is " << ans << endl;
49 }

简直看的头疼是吧?( !!)

现在一点点分析。

首先要记下已经拆开的环。

1 cut[i+1] = 1;

然后如果满足条件,就统计。

1 if (does_not_meet_condition(cnt)) continue;
2 ans = min(ans, cnt);

重头戏在 “判断条件” 函数上。

一步步来。

我们要判断的条件是:1)没圈 2)多环少链 3)没“交际花”

我用数组存边,速度更快。struct 慢慢慢。

判断是不是拆掉的环:

1 if (!cut[G[ring][i]])

这里统计每个环连接其他环的数目:

1 bool must_contains_circle = true;
2     for (int ring = 1; ring <= n; ring++) if (!cut[ring]) {
3         for (int i = 0; i < id[ring]; i++) {
4             if (!cut[G[ring][i]]) { cnt[ring]++; }
5         }
6         if (cnt[ring] > 2) return true;
7         if (cnt[ring] <= 1) must_contains_circle = false;
8     }
9     if (must_contains_circle) { return true; } // a chain without a ring connect with only one another ring is certainly a circle

那么这个 “must_contain_circle” 是什么呢?( ??)

如果每个环都与两个别的环(或者更多的)相邻,那么必定有至少一个圈。否则不一定有一个圈。这是一个优化。

下面判断到底有没有环。

用”染色法“。从链的一端(也就是 cnt 为 1 的点)开始染。

1 int number_of_subchains = 0;
2 memset(mark, 0, sizeof(mark));
3 for (int ring = 1; ring <= n; ring++) if (!cut[ring] && !mark[ring] && cnt[ring] <= 1) {
4     // mark all rings connect with an ‘end‘
5     mark_rings(ring, 0);
6     number_of_subchains++;
7 }
8 for (int ring = 1; ring <= n; ring++) if (!cut[ring] && !mark[ring]) return true; // an unmarked ring implies a circle

如果你吧所有在某根链上的点都染色了(包括一个单独的点,但要弄清它与拆下的点的区别),但还是有那么一些点没染色,肯定有一个环。

顺便记下链的数量,染一次加一。

染色用 dfs 搞定。注意不要陷入循环,也不要漏涂。

1 void mark_rings(int u, int fa) {
2     mark[u] = 1;
3     for (int i = 0; i < id[u]; i++) if(!cut[G[u][i]] && G[u][i] != fa) {
4         mark_rings(G[u][i], u);
5     }
6 }

??。最后一步,判断环数与链数。因为一个拆环可以把两根链连接起来,那么就有 “ 链数 <= 环数 - 1 ”。

1 // number of chains greater than number of cuts plus one
2 return number_of_subchains > number_of_cuts+1;

注意因为我们在这里试问符不符合条件,因此不等号要反向。

终于搞定了。建议在回头看看总体的算法,加深印象。

这题没什么优化。最大的优化本该是用迭代加深的。但我太弱,没写。以后再补。

还有一点非常非常重要。在见图的时候一定要一定要判重。一对环就不要连两次了。否则 cnt 值会变大,可能误判环。

1 void connect(int x, int y) {
2     if (!connection[x][y]) {
3         connection[x][y] = connection[y][x] = 1;
4         G[x][id[x]++] = y;
5         G[y][id[y]++] = x;
6     }
7 }

现在每一点都达到了。整理一下,测一测数据。提交,AC!

在 Virtual OJ 测的 90 ms。有大神能 0ms 刷过。因该是用了迭代加深吧。回头看看去。

有问题留言哈,就算捧场了。

还有如果要转载也告诉我一声(~~)

如果你对这篇文章感兴趣,请关注我,我会定期(每天)更新文章。希望一起交流哈~

2018-01-22 00:06:40

原文地址:https://www.cnblogs.com/Alrond/p/8325964.html

时间: 2024-11-12 10:30:08

UVa 818 Cutting Chains 题解的相关文章

UVA 818 Cutting Chains (DFS)

What a find! Anna Locke has just bought several links of chain some of which may be connected. They are made from zorkium, a material that was frequently used to manufacture jewelry in the last century, but is not used for that purpose anymore. It ha

UVA 818 Cutting Chains

题意就是给一张无向图,去掉某些结点,然后连成一条链,问最少去掉几个结点. n很小n<=15,所以直接枚举2^15个状态就行啦. 链的条件是1.无环,2.没有度大于2的点,3.把n个散链连起来需要n-1次拼接,去掉的结点数>=n-1. #include<bits/stdc++.h> using namespace std; const int maxn = 15; int G[maxn][maxn]; int n; int c[maxn]; bool dfs(int u,int s,

UVA - 818 Cutting Chains 暴力

题目大意:给出n个环(类似奥运五环的那种环),要求你打开其中的m个环,然后以这m个环为中介,使得所有的环能形成一条链 解题思路:暴力枚举,用二进制表示断开的环的位置和数量. 断开环后,和该环相连都断开了,也就是该环变成了一个孤立的环 接着判断一下非断开的环能否连成一个环,如果能连成一个环,那就不可能通过m个环当中介连成一条链 还得判断一下非断开的环的度,如果度超过2,也不能变成一条链 #include<cstdio> #include<cstring> #include<al

uva 818 (位运算 + 判环)

 Cutting Chains  What a find! Anna Locke has just bought several links of chain some of which may be connected. They are made from zorkium, a material that was frequently used to manufacture jewelry in the last century, but is not used for that purpo

uva 10003 Cutting Sticks 简单区间dp

// uva 10003 Cutting Sticks 区间dp // 经典的区间dp // dp(i,j)表示切割小木棍i-j所需要的最小花费 // 则状态转移为dp(i,j) = min{dp(i,k) + dp(k,j) + a[j]-a[i]) // 其中k>i && k<j // a[j] - a[i] 为第一刀切割的代价 // a[0] = 0,a[n+1] = L; // dp数组初始化的时候dp[i][i+1]的值为 0,这表示 // 每一段都已经是切割了的,不

codechef Cutting Recipes题解

Cutting Recipes The chef has a recipe he wishes to use for his guests, but the recipe will make far more food than he can serve to the guests. The chef therefore would like to make a reduced version of the recipe which has the same ratios of ingredie

uva 10003 Cutting Sticks (DP)

uva 10003 Cutting Sticks Description 你的任务是替一家叫Analog Cutting Machinery (ACM)的公司切割木棍.切割木棍的成本是根据木棍的长度而定.而且切割木棍的时候每次只切一段.很显然的,不同切割的顺序会有不同的成本.例如:有一根长10公尺的木棍必须在第2.4.7公尺的地方切割.这个时候就有几种选择了.你可以选择先切2公尺的地方,然后切4公尺的地方,最后切7公尺的地方.这样的选择其成本为:10+8+6=24.因为第一次切时木棍长10公尺,

UVA 10003 Cutting Sticks(区间dp)

Description  Cutting Sticks  You have to cut a wood stick into pieces. The most affordable company, The Analog Cutting Machinery, Inc. (ACM), charges money according to the length of the stick being cut. Their procedure of work requires that they onl

uva 10003 Cutting Sticks 【区间dp】

题目:uva 10003 Cutting Sticks 题意:给出一根长度 l 的木棍,要截断从某些点,然后截断的花费是当前木棍的长度,求总的最小花费? 分析:典型的区间dp,其实和石子归并是一样的,花费就是石子的和,那么久不用多说了. AC代码: #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include <map> #include <