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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值