【HDU6150 2017中國大學生程序設計競賽 - 網絡選拔賽 A】【構造】Vertex Cover

Vertex Cover

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others)
Total Submission(s): 131    Accepted Submission(s): 48
Special Judge


Problem Description
As we know, minimumvertexcover is a classical NP-complete problem. There will not be polynomial time algorithm for it unless P=NP.

You can see the definition of vertex cover in https://en.wikipedia.org/wiki/Vertex_cover.

Today, little M designs an "approximate" algorithm for vertex cover. It is a greedy algorithm. The main idea of her algorithm is that always choosing the maximum degree vertex into the solution set. The pseudo code of her algorithm is as follows:

We assume that the labels of the vertices are from 1 to n.
for (int i = 1; i <= n; ++i) {
  use[i] = false;
    deg[i] = degree of the vertex i;
}
int ans = 0;
while (true) {
  int mx = -1, u;
    for (int i = 1; i <= n; ++i) {
      if (use[i])
          continue;
        if (deg[i] >= mx) {
          mx = deg[i];
            u = i;
        }
    }
    if (mx <= 0)
      break;
    ++ans;
    use[u] = true;
    for (each vertex v adjacent to u)
      --deg[v];
}
return ans;


As a theory computer scientist, you immediately find that it is a bad algorithm. To show her that this algorithm dose not have a constant approximate factor, you need to construct an instance of vertex cover such that the solution get by this algorithm is much worse than the optimal solution.

Formally, your program need to output a simple undirected graph of at most 500 vertices. Moreover, you need to output a vertex cover solution of your graph. Your program will get Accept if and only if the solution size get by the above algorithm is at least three times as much as yours. 
 

Input
There is no input.
 

Output
First, output two integer n and m in a line, separated by a space, means the number of the vertices and the number of the edges in your graph.
In the next m lines, you should output two integers u and v for each line, separated by a space, which denotes an edge of your graph. It must be satisfied that 1u,vn and your graph is a simple undirected graph.
In the next line, output an integer k(1kn), means the size of your vertex cover solution.
Then output k lines, each line contains an integer u(1un) which denotes the label of a vertex in your solution. It must be satisfied that your solution is a vertex cover of your graph.
 

Sample Output
4 4 1 2 2 3 3 4 4 1 2 1 3
Hint
The sample output is just to exemplify the output format.
 

Source

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 512, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
vector< pair<int, int> >edge;
int ind[N];
vector<int>vt[N];
int check()
{
	bool use[N];
	int deg[N];
	for (int i = 1; i <= 256; ++i)
	{
		use[i] = false;
		deg[i] = ind[i - 1];
	}
	int ans = 0;
	while (true)
	{
		int mx = -1, u;
		for (int i = 1; i <= 256; ++i)
		{
			if (use[i])continue;
			if (deg[i] >= mx)
			{
				mx = deg[i];
				u = i;
			}
		}
		if (mx <= 0)break;
		++ans;
		use[u] = true;
		for (auto v : vt[u])
		{
			--deg[v];
		}
	}
	return ans;
}
void solve()
{
	int bas = 64;
	for (int rgt_num = 32; rgt_num >= 1; rgt_num /= 2)
	{
		int lft_num = rgt_num / 2;
		if (lft_num == 0)lft_num = 1;
		for (int x = 0; x < 64; ++x)
		{
			int y = x / 2;
			int d = 32 / lft_num;
			for (int tim = 1; tim <= lft_num; ++tim)
			{
				edge.push_back({ x, bas + y });
				++ind[x];
				++ind[bas + y];
				vt[x + 1].push_back(bas + y + 1);
				vt[bas + y + 1].push_back(x + 1);
				y += d;
				y %= 32;
			}
		}
		bas += 32;
	}
	int n = 64 * 4; int m = edge.size();
	printf("%d %d\n", n, m);
	for (auto it : edge)
	{
		printf("%d %d\n", it.first + 1, it.second + 1);
	}
	printf("%d\n", 64);
	for (int i = 0; i < 64; ++i)
	{
		printf("%d\n", i + 1);
	}
}
int main()
{
	solve();
	return 0;
}
/*
【題意】
http://acm.hdu.edu.cn/showproblem.php?pid=6150
"最小點覆蓋集"是個NP完全問題
有一個近似算法是說————
每次選取度數最大的點(如果有多個這樣的點,則選擇最後一個)
讓你構造一個圖,使得其近似算法求出來點數是你給定的覆蓋點數的至少3倍。

【分析】
即,我們要構造一種覆蓋方案,使得我們選取的點數儘可能少,其算法選取的點數儘可能多。

這裏比較容易想到二分圖。
然後二分圖的最小答案肯定只要是min(左邊點數,右邊點數)

我們不妨使得左邊的點數比較少,右邊的點數比較多。
在這種情況下,我們的選擇方案就可以是左邊的所有點。
而我們希望其貪心策略選擇的儘量是右邊的點。

==========================================================================
我的構造方案——見代碼
左邊64個點,右邊6組,每組32個點,每個點的邊數依次爲{32, 16, 8, 4, 2, 1}
==========================================================================
ICPC Camp解法
左邊n個點(編號較小),
對於每個i∈[1, n],右邊新建n / i 個點,每個點選擇左邊i個點連一條邊(總邊數爲n^2),使得每個i最多隻使得左邊每個點的度數+1
這樣左邊每個點的度數都是<=n的,而右邊點每個點的度數會分別是{1 } {2 } {3 } {4 } ... {n }
這樣每次會選右邊的點去除,去除之後左邊的度數依舊<=右邊的度數

於是貪心做法會選擇右邊,大小爲nlogn的點集,
而實際只需要選擇左邊,大小爲n的點集即可。

【時間複雜度&&優化】
O(n ^ 2)

*/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章