spoj spoj839 Optimal Marks 网络流 最小割 附数据生成器

题目大意:


给定一张图,每个点有一个数字,有的点没有数字。


给所有点涂上数字,使得任意直接相连的2个点的数字,xor值的和最小。



对于xor运算,每一位之间没有关系。所以题目首先拆分成31个小问题。。求最后每个数字的每一位(二进制位)的数字。


然后目前的问题就是,一张图,很多点数字是0,很多点数字是1,还有一些点没有上数字。 找一个方案,使得任何直接相连的点的数字,xor值的和最小。


显然0附近的点都写0比较好,1附近的点都写1比较好。 最后1和0交汇了,希望交汇的点尽量小。 这显然就是一个最小割。


然后新原连1的边,0连新汇,流量都是无穷大。


其他原来图中相连的边,都是正反连上流量为1的边即可。


跑一个最大流,割的位置,就是01交汇的地方。


然后从S出发经过的所有点,都涂上1. 其他点都是0.


然后这题就做完了。。


写完程序过了样例交一发WA了。。就去写对拍器。。结果发现数组没清0,导致多组数据WA……忧伤


数据生成器代码

#include <cstdio>
#include <cstring>
#include <vector>
#include <ctime>
#include <iostream>
#include <cstdlib>
using namespace std;

char get_rand()
{
	int flag= rand()%3;
	if (flag==0)
	{
		return rand()%10 + '0';
	}
	if (flag==1)
	{
		return rand()%26+'a';
	}
	return rand()%26+'A';
}

int rand_from_to(int a, int b)
{
	return rand()%(b-a+1) + a;
}

int f[550][550];
typedef pair<int,int>pii;
#define mp(a,b) make_pair(a,b)

int main()
{
	srand(time(0));

	int T=2;
	cout<<T<<endl;
	while (T--)
	{
		memset(f,0,sizeof(f));
		int n =  5;//n个点,m个边
		int totm = 15;
		int sb=rand()%totm+1;
		for (int i = 1; i <= sb;++i)
		{
			int a = rand()%n+1;
			int b = rand()%n+1;
			if (a>b)swap(a,b);
			f[a][b] = rand()*rand()%10000000+1;
		}

		vector<pii>g;
		for (int i = 1; i <=n;++i)
			for (int j = i+1; j <=n;++j)
			{
				if (i==j)continue;
				if (f[i][j])
				{
					g.push_back(mp(i,j));
					f[j][i]=0;
				}
			}
		cout<<n<<" ";
		cout<<g.size()<<endl;
		for (auto x : g)
		{
			cout<<x.first<<" "<<x.second<<endl;//<<" "<<f[x.first][x.second]<<endl;
		}
		g.clear();
		for (int i = 1; i <= n ; ++ i)
		{
			int flag = rand()%2;
			if (flag)	continue;
			g.push_back(mp(i, rand()%10));
		}

		cout<<g.size()<<endl;
		for (auto x : g)
		{
			cout<<x.first<<" "<<x.second<<endl;//<<" "<<f[x.first][x.second]<<endl;
		}
	}
	return 0;
}


ac code

#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#include <tr1/unordered_map>
using std::tr1::unordered_map;
//using std::setiosflags;
//using std::setprecision;
using std::sort;
using std::max;
using std::min;
using std::cout;
using std::stack;
using std::cin;
using std::endl;
using std::swap;
using std::pair;
using std::vector;
using std::set;
using std::map;
using std::make_pair;
using std::multiset;
using std::unique;
using std::queue;
using std::greater;
using std::string;
using std::priority_queue;
using std::lower_bound;//返回第一个不小于
using std::upper_bound;//返回第一个大于
using std::max_element;
using std::min_element;
using __gnu_pbds::pairing_heap_tag;
#define x first
#define y second
#define Hash unordered_map
#define clr(x,b) memset((x),(b),sizeof(x))
typedef unsigned long long uLL;
typedef long long LL;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef __gnu_pbds::priority_queue<pii, greater<pii>, pairing_heap_tag> Heap;//小根堆
typedef Heap::point_iterator Hit;
const Hit null;
const double PI = acos(-1);
const LL LINF = 0x3f3f3f3f3f3f3f3fll;//4e18
const int INF = 0x3f3f3f3f;//1e9
const double eps = 1e-8;
const int MOD = 1 << 16;
#define prln(x) cout<<#x<<" = "<<x<<endl
#define pr(x) cout<<#x<<" = "<<x<<" "

//调用方法:
//init(n) 初始化 n 为节点数
//clear_flow() 清空所有边的流量
//add_edge(from, to, cap) 添加边from -> to 容量为cap
//maxflow(s, t) 返回s->t的最大流
//void mincut(vector<int>& ans) // 调用完maxflow后才可以用,ans里面存最小割
//print() 打印整张图,调试用
const int maxn = 600;
struct Edge { int from, to, cap, flow; };
struct ISAP {
	int n, m, s, t;
	vector<Edge> edges;
	vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
	bool vis[maxn];        // BFS使用
	int d[maxn];           // 从起点到i的距离
	int cur[maxn];        // 当前弧指针
	int p[maxn];          // 可增广路上的上一条弧
	int num[maxn];        // 距离标号计数
	void add_edge(int from, int to, int cap) {
		edges.push_back((Edge){from, to, cap, 0});
		edges.push_back((Edge){to, from, 0, 0});
		m = edges.size();
		G[from].push_back(m-2);
		G[to].push_back(m-1);
	}
	bool bfs() {
		memset(vis, 0, sizeof(vis));
		queue<int> q;
		q.push(t);
		vis[t] = 1;
		d[t] = 0;
		while(!q.empty()) {
			int x = q.front(); q.pop();
			for(int i = 0; i < G[x].size(); i++) {
				Edge& e = edges[G[x][i]^1];
				if(!vis[e.from] && e.cap > e.flow) {
					vis[e.from] = 1;
					d[e.from] = d[x] + 1;
					q.push(e.from);
				}
			}
		}
		return vis[s];
	}

	void doit(int ans[])
	{
		clr(vis, 0);
		queue<int>q;
		q.push(s);
		vis[s] = 1;
		d[s] = 0;
		while (!q.empty())
		{
			int x = q.front();
			ans[x]+=1;
			q.pop();
			for (int i = 0; i < G[x].size(); i ++ )
			{
				Edge& e = edges[G[x][i]];
				if (!vis[e.to] && e.cap > e.flow)
				{
					vis[e.to] = 1;
					q.push(e.to);
				}
			}
		}
	}




	void init(int n) {
		this->n = n;
		for(int i = 0; i < n; i++) G[i].clear();
		edges.clear();
	}
	void clear_flow() {
		for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;    
	}
	int augment() {
		int x = t, a = INF;
		while(x != s) {
			Edge& e = edges[p[x]];
			a = min(a, e.cap-e.flow);
			x = edges[p[x]].from;
		}
		x = t;
		while(x != s) {
			edges[p[x]].flow += a;
			edges[p[x]^1].flow -= a;
			x = edges[p[x]].from;
		}
		return a;
	}
	int maxflow(int s, int t) {//到的最大流大于need就停止,如果没有限制,删去含有need的地方
		this->s = s; this->t = t;
		int flow = 0;
		bfs();
		memset(num, 0, sizeof(num));
		for(int i = 0; i < n; i++) num[d[i]]++;
		int x = s;
		memset(cur, 0, sizeof(cur));
		while(d[s] < n) {
			if(x == t) {
				flow += augment();
				x = s;
			}
			int ok = 0;
			for(int i = cur[x]; i < G[x].size(); i++) {
				Edge& e = edges[G[x][i]];
				if(e.cap > e.flow && d[x] == d[e.to] + 1) { // Advance
					ok = 1;
					p[e.to] = G[x][i];
					cur[x] = i; // 注意
					x = e.to;
					break;
				}
			}
			if(!ok) { // Retreat
				int m = n-1; // 初值注意
				for(int i = 0; i < G[x].size(); i++) {
					Edge& e = edges[G[x][i]];
					if(e.cap > e.flow) m = min(m, d[e.to]);
				}
				if(--num[d[x]] == 0) break;//gap优化
				num[d[x] = m+1]++;
				cur[x] = 0; // 注意
				if(x != s) x = edges[p[x]].from;
			}
		}
		return flow;
	}

	void mincut(vector<int>& ans) { // 调用完maxflow后才可以用,ans里面存最小割
		bfs();
		for(int i = 0; i < edges.size(); i++) {
			Edge& e = edges[i];
			if(!vis[e.from] && vis[e.to] && e.cap > 0) ans.push_back(i);
		}
	}
	void print() {
		printf("Graph:\n");
		for(int i = 0; i < edges.size(); i++)
			printf("%d->%d, %d, %d\n", edges[i].from, edges[i].to , edges[i].cap, edges[i].flow);
	}
} isap;

int n, m;


struct xxx
{
	int u,v;
}eee[3000*2];
int known_num;
int isbiao[600];//是否初始有标号
int biao[600];//如果被标了,标的是什么
int marks[600];
int ans[600];
int mp[600][600];

void init()
{
	scanf("%d%d", &n, &m);
	clr(mp,0);
	for (int i = 0; i != m; ++ i)
	{
		scanf("%d%d", &eee[i].u, &eee[i].v);
		mp[eee[i].u][eee[i].v]=1;
		mp[eee[i].v][eee[i].u]=1;
	}
	scanf("%d", &known_num);
	clr(isbiao, 0);
	clr(ans, 0);
	for (int i = 1; i <= known_num; ++ i)
	{
		int a,b;
		scanf("%d%d", &a, &b);
		isbiao[a]=1;
		biao[a]=b;
	}
}


void cal()
{
	isap.init(n + 5);
	for (int i = 0; i <=n; ++ i)
		for (int j = i+1; j <=n;++j)
		{
			if (mp[i][j])
			{
				isap.add_edge(i, j, 1);
				isap.add_edge(j, i, 1);
			}
		}
	for (int i = 1; i <= n; ++ i)
	{
		if (!isbiao[i])	continue;
		if (marks[i])
		{
			isap.add_edge(0, i, INF);
		}else
		{
			isap.add_edge(i, n + 1, INF);
		}
	}
	isap.maxflow(0, n + 1);

}

void doit()
{
	clr(ans, 0);	//初始化标号为0
	for (int i = 1; i <= 31; ++ i)
	{
		for (int j = 1; j <= n; ++ j)
		{
			ans[j]<<=1;
			if (!isbiao[j])	continue;
			marks[j] = (biao[j]>>(31-i))&1;
		}
		cal();
		isap.doit(ans);
	}
	for (int i = 1; i <= n; ++ i)
		printf("%d\n", ans[i]);
}


int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		init();
		doit();
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值