原题地址:http://poj.org/problem?id=1944
题目大意:有n个点排成一圈,可以连接任意两个相邻的点,给出 p 对点,要求这 p 对点必须直接或间接相连,求最少的连接边数
数据范围:n <= 1000, p <= 10000
算法分析:
一开始当最小生成树做的,才发现自己 SB 了……
先考虑不是环形而是线形的结构,直接贪心连接每两个点之间的所有点就好了。这样我们可以枚举环形的断点,然后逐次贪心,求最小解即可
很多同学在贪心的时候应用了线段树是复杂度高达O(np log n),其实丝毫没有必要,我们只需要每次断点时生成一个数组,在每对点的左边点处加1,再在右边点处减1,然后求一下部分和,部分和中正数的个数即为所求(详见代码)
参考代码:
1 //date 20140205 2 #include <cstdio> 3 #include <cstring> 4 5 const int maxn = 1005; 6 const int maxp = 10005; 7 const int INF = 0x7F7F7F7F; 8 9 inline void swap(int &a, int &b){int x = a; a = b; b = x;} 10 inline int min(int a, int b){return a < b ? a : b;} 11 12 int n, p; 13 int pa[maxp][2]; 14 int s[maxn << 1]; 15 16 int main() 17 { 18 scanf("%d%d", &n, &p); 19 for(int i = 1; i <= p; ++i) 20 { 21 scanf("%d%d", &pa[i][0], &pa[i][1]); 22 if(pa[i][0] > pa[i][1])swap(pa[i][0], pa[i][1]); 23 } 24 25 int ans = INF; 26 for(int i = 1; i <= n; ++i) 27 { 28 memset(s, 0, sizeof s); 29 for(int j = 1; j <= p; ++j) 30 { 31 int x = pa[j][0], y = pa[j][1]; 32 if(x <= i)x += n; 33 if(y <= i)y += n; 34 if(x > y)swap(x, y); 35 ++s[x]; --s[y]; 36 } 37 int now = 0, res = 0; 38 for(int j = 1; j <= n; ++j) 39 { 40 now += s[i + j]; 41 if(now > 0)++res; 42 } 43 ans = min(ans, res); 44 } 45 printf("%d ", ans); 46 return 0; 47 }