SPOJ839



OPTM - Optimal Marks

no tags 

You are given an undirected graph G(V, E). Each vertex has a mark which is an integer from the range [0..231 – 1]. Different vertexes may have the same mark.

For an edge (u, v), we define Cost(u, v) = mark[u] xor mark[v].

Now we know the marks of some certain nodes. You have to determine the marks of other nodes so that the total cost of edges is as small as possible.

Input

The first line of the input data contains integer T (1 ≤ T ≤ 10) - the number of testcases. Then the descriptions of T testcases follow.

First line of each testcase contains 2 integers N and M (0 < N <= 500, 0 <= M <= 3000). N is the number of vertexes and M is the number of edges. Then M lines describing edges follow, each of them contains two integers u, v representing an edge connecting u and v.

Then an integer K, representing the number of nodes whose mark is known. The next K lines contain 2 integers u and p each, meaning that node u has a mark p. It’s guaranteed that nodes won’t duplicate in this part.

Output

For each testcase you should print N lines integer the output. The Kth line contains an integer number representing the mark of node K. If there are several solutions, you have to output the one which minimize the sum of marks. If there are several solutions, just output any of them.

Example

Input:
1
3 2
1 2
2 3
2
1 5
3 100

Output:
5
4
100

二進制 +  網絡流

題目大意如下:

給定N個點M條邊,每個點有權值,每條邊的權值爲該邊連接的兩點的點權的異或值,其中某些點的權值已經確定,其他點的權值在0 to 2 ^ 31 - 1內,問使整個網絡邊權和最小的點權方案。

考慮到兩個數的異或值實際上是每個二進制位分別異或,又點權範圍爲二的整數次冪減一,即不存在高位爲1則低位必須爲0的情況,即數的二進制位每一位取值互不影響。

於是我們原問題轉化爲分別確定每個數每個二進制位是0或1.

要求邊權和最小,即兩端點權分別爲0和1的邊最少,那麼若將邊的容量定爲1,則可以轉化爲最小割問題來解決。將原圖中的無向邊變爲權值爲1的雙向邊,從S向該位確定爲1的點連邊,容量爲無窮大,從該位確定爲0的點向T連邊,容量爲無窮大(這樣可以確保這些邊不會在最小割上)。

則該網絡的最小割即爲該二進制位邊權和的最小值,因爲最小割將網絡分爲兩部分,與S相連的部分的點該位點權爲1,與T相連的部分的點該位點權爲0,則最小割刪掉最少的邊使該網絡不聯通(因爲邊權爲1),且割去的邊代表該邊該位權值爲1.

於是按照二進制位最多進行31次最小割,即可得出答案。

(PS:題目要求輸出所有答案中點權和最小的某一組,即每位的1儘可能少,那麼跑完最小割後從S開始DFS,將經過的點該位點權設爲1)

代碼如下:

/* 
* @Author: duyixian
* @Date:   2015-04-22 16:19:40
* @Last Modified by:   duyixian
* @Last Modified time: 2015-04-22 16:57:06
*/

#include "cstdio"
#include "cstdlib"
#include "iostream"
#include "algorithm"
#include "queue"
#include "cstring"

using namespace std;

#define MAX_SIZE 505
#define INF 0x3F3F3F3F

struct Edge
{
	int Next, To, C;
}Edges[MAX_SIZE * MAX_SIZE * 2];

struct Path
{
	int From, To;
	bool operator < (const Path &a) const
	{
		if(From < a.From)
			return true;
		else if(From == a.From)
			return To < a.To;
		else
			return false;
	}
	bool operator == (const Path &a) const
	{
		return (From == a.From && To == a.To);
	}
}Paths[3005];

int Front[MAX_SIZE], Distance[MAX_SIZE], Mark[MAX_SIZE], Num[MAX_SIZE], Visited[MAX_SIZE];
int Total, N, M, K, S, T;

inline void Add_Edge(int From, int To, int C)
{
	++Total;
	Edges[Total].To = To;
	Edges[Total].C = C;
	Edges[Total].Next = Front[From];
	Front[From] = Total;
}

inline bool BFS()
{
	memset(Distance, 0, sizeof(Distance));
	Distance[S] = 1;
	queue<int> Queue;
	Queue.push(S);
	while(!Queue.empty())
	{
		int Now = Queue.front();
		Queue.pop();
		for(int temp = Front[Now]; temp; temp = Edges[temp].Next)
		{
			if(Edges[temp].C && !Distance[Edges[temp].To])
			{
				Distance[Edges[temp].To] = Distance[Now] + 1;
				Queue.push(Edges[temp].To);
			} 
		}
	}
	return Distance[T];
}

int DFS(int Now, int In)
{
	if(Now == T)
		return In;
	int Rest = In;
	for(int temp = Front[Now]; temp; temp = Edges[temp].Next)
	{
		if(Edges[temp].C && Distance[Edges[temp].To] == Distance[Now] + 1)
		{
			int Increment = DFS(Edges[temp].To, min(Edges[temp].C, Rest));
			Rest -= Increment;
			Edges[temp].C -= Increment;
			Edges[temp ^ 1].C += Increment;
			if(!Rest)
				return In;
		}
	}
	if(Rest == In)
		Distance[Now] = 0;
	return In - Rest;
}

void Build()
{
	memset(Front, 0, sizeof(Front));
	Total = 1;
	S = 0;
	T = N + 1;
	for(int i = 1; i <= M; ++i)
	{
		if(Paths[i] == Paths[i - 1])
			continue;
		Add_Edge(Paths[i].From, Paths[i].To, 1);
		Add_Edge(Paths[i].To, Paths[i].From, 1);
	}
	for(int i = 1; i <= N; ++i)
	{
		if(Mark[i] == -1)
			continue;
		int temp = Mark[i] & 1;
		Mark[i] >>= 1;
		if(temp)
		{
			Add_Edge(S, i, INF);
			Add_Edge(i, S, 0);
		}
		else
		{
			Add_Edge(i, T, INF);
			Add_Edge(T, i, 0);
		}
	}
}

inline int Max_Flow()
{
	int Ans = 0;
	Build();
	while(BFS())
		Ans += DFS(S, INF);
	return Ans;
}

void Input()
{
	memset(Mark, -1, sizeof(Mark));
	memset(Num, 0, sizeof(Num));
	cin >> N >> M;
	for(int i = 1; i <= M; ++i)
	{
		int temp1, temp2;
		scanf("%d%d", &temp1, &temp2);
		Paths[i].From = min(temp1, temp2);
		Paths[i].To = max(temp1, temp2);
	}
	sort(Paths + 1, Paths + M + 1);
	cin >> K;
	for(int i = 1; i <= K; ++i)
	{
		int temp1, temp2;
		scanf("%d%d", &temp1, &temp2);
		Mark[temp1] = temp2;
	}
}

int Find(int Now, int Power)
{
	Visited[Now] = true;
	Num[Now] += (1 << Power);
	for(int temp = Front[Now]; temp; temp = Edges[temp].Next)
	{
		if(!Visited[Edges[temp].To] && Edges[temp].C)
		{
			Find(Edges[temp].To, Power);
		}
	}
}

void Solve()
{
	for(int i = 0; i < 31; ++i)
	{
		int Flow = Max_Flow();
		memset(Visited, 0, sizeof(Visited));
		Find(S, i);
	}
	for(int i = 1; i <= N; ++i)
		printf("%d\n", Num[i]);
}

int main()
{
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w",stdout);
	int T;
	cin >> T;
	while(T--)
	{
		Input();
		Solve();
	}
	fclose(stdout);
	fclose(stdin);
	return 0;
}


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