Gym - 100204G Network Wars 网络流相关定理 附一组数据

题目大意:


给定n个点,m个无向边。每个边有权。


求一个割集,可以让1号点,不能连到n号点。 并且,这个割集的平均值最小。(选了k个点,权和为p。那么就是p/k)


做法:

简述一个定理:

求f(x)=min{ a(x)/b(x) }  (b(x)>0)

假设g=f(x)=min{a(x)/b(x)}

那么g*b(x)=a(x)

令:h(g)=min{a(x)-g*b(x)}

则,函数h(g)为0的时候,对应的x的值,为min{ a(x)/b(x) }

同时,h(g)函数,随着g的增加,h(g)是严格减函数。


利用二分答案,这题就可以做出来了


附一组数据

100 146
1 2 2019021
1 5 9088577
1 7 4505047
1 9 5535799
2 3 1307581
2 4 9782673
2 21 5412393
2 26 6812581
2 35 2665918
3 10 9034231
3 23 3899509
3 28 7304833
3 30 8585616
3 39 8199873
3 57 9924001
3 60 5545900
3 72 1084141
4 6 6170481
4 15 3963249
4 33 4498626
4 60 6041601
4 61 1767485
4 73 7553972
5 6 1341317
5 36 4729337
6 7 3656061
6 12 9317391
6 13 6245861
6 30 4426237
6 87 3022766
7 8 3635862
7 17 1014537
7 19 5278756
7 55 1707995
8 11 6267273
8 64 6130361
8 83 5544759
9 43 1795275
9 60 3139757
9 78 4742951
10 20 3715627
10 23 6412437
10 29 8732489
10 82 5430456
10 89 9579191
10 90 7678448
11 18 2837380
11 24 1208377
11 73 9123535
12 22 1340363
12 98 3366114
13 14 7340421
13 27 963711
14 54 4354601
14 94 9325465
15 16 443806
15 19 6394469
15 37 1359787
15 79 8407345
16 40 462049
16 65 9435297
16 86 5951364
16 99 5931605
17 48 5635975
17 66 6237661
18 34 6018921
18 100 3506047
20 25 6988205
20 59 6822257
20 68 9000063
20 71 2419847
21 51 9131224
21 58 8834708
21 91 2465505
23 33 338905
23 78 4565111
24 31 6880644
25 38 4775785
25 54 1072608
25 60 5142715
26 44 5402699
26 91 1221046
27 28 5630565
27 32 1766197
27 90 4465977
28 62 4843877
29 36 5359557
29 80 9055801
30 32 5265687
30 53 1112947
31 67 6354711
31 75 5375204
31 88 2851841
33 49 3496191
33 76 8394091
33 93 8856504
34 46 493818
34 94 838731
35 42 8242881
35 75 7502385
36 41 5706099
36 64 8672343
36 77 3266697
36 84 354905
37 40 1913761
39 50 2165838
39 66 134069
41 73 2320882
41 87 1083117
42 93 8451501
43 45 6430445
43 70 8059470
44 56 5002219
45 47 5820773
45 70 9217644
45 91 5682251
45 98 7767844
46 52 1007531
46 81 6090545
46 83 2335581
47 53 8792843
49 88 2457529
50 54 2280171
50 75 7817601
51 77 712111
52 77 8597432
53 80 4707905
54 64 1318081
57 63 4238122
57 72 1705501
60 64 1172261
60 68 1972709
60 69 7623342
65 80 94515
65 96 1607893
66 74 5674046
71 97 125038
72 74 9330113
75 92 8278586
76 88 5904813
78 95 1053617
79 83 8393803
81 85 6937833
82 87 8094478
82 99 6217892
89 96 8481801


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) memset(x,0,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<<" "

int n, m;
int vis[500];
//调用方法:
//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 = 120;
struct Edge { int from, to; double cap, flow; int id;};
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, double cap, int id) {
		edges.push_back((Edge){from, to, cap, 0, id});
		edges.push_back((Edge){to, from, 0, 0, id});
		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 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;    
	}
	double augment() {
		int x = t;
		double 	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;
	}
	double maxflow(int s, int t) {//到的最大流大于need就停止,如果没有限制,删去含有need的地方
		this->s = s; this->t = t;
		double 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));
		//print();
		while(d[s] < n) {
			if(x == t) {
				flow += augment();
				//if(flow >= need) return flow;
				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(edges[i].id);
		}
	}
	
	void print() {
		printf("Graph:\n");
		for(int i = 0; i < edges.size(); i++)
			printf("%d->%d, %.5lf, %.5lf\n", edges[i].from, edges[i].to , edges[i].cap, edges[i].flow);
	}
	
	vector<int>output;
	void doit(int vis[], int m)
	{
		mincut(output);
		/*
		for (auto x:output)
		{
			cout<<x<<" ";
		}
		*/
		//cout<<endl;
		//print();
		

		//prln('!');

		for (int i = 0; i <= m; ++ i)
			if (vis[i])	
			{
				output.push_back(i);
			}
		sort(output.begin(),output.end());

		printf("%d\n", output.size());
		if (!output.size())	return;
		for (int i = 0; i < output.size()-1;++i)
			printf("%d ", output[i]);
		printf("%d\n",output[output.size()-1]);
	}
} isap;

struct EEE
{
	int from, to;
       double	weight;
}gg[500];


double cnt=0;

double check(double lamuda)
{
	isap.init(n+5);
	memset(vis,0,sizeof(vis));
	cnt=0;
	for (int i = 1; i <= m; ++i)
	{
		double w = gg[i].weight - lamuda;
		int s = gg[i].from;
		int t = gg[i].to;
		if (w<=0)
		{
			cnt += w;
			vis[i]=1;//直接是割
			continue;
		}
		isap.add_edge(s, t, w, i);
		isap.add_edge(t, s, w, i);
	}
	//isap.print();
	return cnt+isap.maxflow(1, n);
}

void init()
{
	for (int i = 1; i<=m ; ++i)
	{
		scanf("%d%d%lf", &gg[i].from, &gg[i].to, &gg[i].weight);
	}

/*
	printf("%.5lf\n", check(2));
	isap.doit();
	return ;
	*/

	double L=0,R=1000000000000, mid;
	while (R-L>0.00001)
	{
		//printf("%.5lf %.5lf\n",L,R);
		mid = L + (R-L)/2;
		if (check(mid)<0)	R = mid;
		else L = mid;
		//printf("%.5lf\n", check(mid));
	}
	
	isap.doit(vis,m);
}


int main()
{
	freopen("network.in","r",stdin);
	freopen("network.out","w",stdout);
	while (~scanf("%d%d", &n, &m))
	{
		init();
	}
	return 0;
}



附数据生成器:

#include <cstdio>
#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[150][150];
typedef pair<int,int>pii;
#define mp(a,b) make_pair(a,b)

int main()
{

	int char_num = 5;//字符总数


	srand(time(0));
	int n =  5;//n个点,m个边
	//int maxm= rand()%15+1;
	for (int i = 2; i <= n;++i)
	{
		f[rand_from_to(1,i-1)][i] = rand() * rand() %10000000+1;
	}

	for (int i = 1; i <= rand()%(400-n-1)+1;++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<<" "<<f[x.first][x.second]<<endl;
	}
	return 0;
}


附不严格的SPJ:(没有判断所选值是否为割集~)


#include <bits/stdc++.h>
using namespace std;

int n,m;
int sum=0;
int a[500],b[500],c[500];

int main()
{
	while (1)
	{
		system("make>input.txt");
		system("a<input.txt>a.out");
		system("ac<input.txt>ac.out");
		freopen("input.txt","r",stdin);
		cin>>n>>m;
		for (int i = 1; i<=m;++i)
		{
			cin>>a[i]>>b[i]>>c[i];
		}
		fclose(stdin);
		freopen("a.out","r",stdin);
		int a1n;
		long long suma=0;
		cin>>a1n;
		for (int i= 1;i<=a1n;++i)
		{
			int tmp;
			cin>>tmp;
			suma+=c[tmp];
		}
		fclose(stdin);

		freopen("ac.out","r",stdin);
		int a2n;
		long long sumb=0;
		cin>>a2n;
		for (int i = 1; i <=a2n;++i)
		{
			int tmp;
			cin >> tmp;
			sumb +=c[tmp];
		}
		if (a1n==a2n && !a1n)
			goto clo;
		if (suma/a1n!=sumb/a2n)
		{
			cout<<suma<<" "<<a1n<<endl;
			cout<<sumb<<" "<<a2n<<endl;
			cout<<"WA"<<endl;
			return 0;
		}
clo:;
		fclose(stdin);
	}

}	


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值