51nod 图 1499

题意:

1499 图
2.0 秒 262,144.0 KB 80 分 5级题
给一个无向图,你要将这些点分成A、B两个集合,使得满足A的导出子图是一个完全图,而B的导出子图是一个没有边的图。
但是事实上你不一定能够做到,所以你允许有错误。我们定义一个完美值为:
1.如果A中两点有边相连,则增加|i-j|的完美值。
2.如果B中两点无边相连,则增加|i-j|的完美值。
(i,j是这两个点的编号)
那么,我们让完美值最大就可以了。
N <= 1000, M <= 200000

输入
N, M 表示点数和边数
M行,
u,v表示一条无向边。
(不会有重边和自环)
输出
一个数,表示最大的完美值。
输入样例
5 5
1 2
1 3
1 4
1 5
2 3
输出样例
11

思路:

5555,本菜鸡的最大流和最小割确实是不熟练。。。。5555,所以我根本不知道这是最小割,完败!!!

(1)虽然已经看过了,那么我们再来熟悉一下什么是最小割

在这里插入图片描述

(2)那么我们再康康这道题的题意:把n个顶点划分为两个集合A和B,一个集合的导出子图是完全图,另一个的导出子图是没有边的图。定义一个完美值为:1.如果A中两点有边相连,则增加|i-j|的完美值。2.如果B中两点无边相连,则增加|i-j|的完美值。(i,j是这两个点的编号),求最大的完美值。

(3)正确解法就是将顶点划分为S和T两个集合,S是两两之间没有边的集合,T是两两之间都有边的集合,那么我们可以这样求
在这里插入图片描述

明显的最小割!!!但是我却不会!!!现在还行吧

代码实现:

//正规解法
#include <cstdio>
#include<iostream>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cmath>
#include<cstring>
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
using namespace std;
typedef long long ll;

const int maxn = 2e5 + 5;
const ll inf = 1e18;

int n,m;
struct node{
	int to;
	int w;
	int rev;//反向边的位置
};
vector<node>G[maxn];
int dep[maxn];
int cr[maxn];
queue<int> q;
int vis[1005][1005];

void add(int u,int v,int w){
	G[u].push_back((node){v,w,G[v].size()});
	G[v].push_back((node){u,w,G[u].size() - 1});
}

bool bfs(int s,int t){
	memset(dep,0,sizeof(dep));
	memset(cr,0,sizeof(cr));
	while(!q.empty()) q.pop();
	dep[s] = 1; q.push(s);
	while(!q.empty()){
		int now = q.front(); q.pop();
		int len = G[now].size();
		for(int i = 0;i < len;i++){
			node cur = G[now][i];
			if(cur.w > 0&&!dep[cur.to]){//寻找可行的路径
				q.push(cur.to); dep[cur.to] = dep[now] + 1;
			}
		}
	}
	return dep[t] != 0;
}

ll dfs(int s,int t,ll mx){
	if(s == t) return mx;
	ll flow = 0; int len = G[s].size(); int tmp;
	for(int &i = cr[s];i < len;i++){//接着上一次查找的位置继续往下找
		node &e = G[s][i];
		if(dep[s] + 1 == dep[e.to]&&e.w > 0){//可能可行的路径
			tmp = dfs(e.to,t,min(mx,(ll)e.w));
			if(tmp != 0){
				mx -= tmp; e.w -= tmp; flow += tmp;
				G[e.to][e.rev].w += tmp;
			}
			if(mx == 0) break;
		}
	}
	if(flow == 0) dep[s] = 0;
	return flow;
}

ll dinic(int s,int t){
	ll flow = 0;
	while(bfs(s,t)) flow += dfs(s,t,inf);//最大流累加
	return flow;
}

int main(){
	while(~scanf("%d%d",&n,&m)){
		for(int i = 1;i <= n;i++) G[i].clear();
		int u,v;
		for(int i = 1;i <= m;i++){
			scanf("%d%d",&u,&v);
			vis[u][v] = 1;
			vis[v][u] = 1;
		}
		int sum = 0;
		for(int i = 1;i <= n;i++){
			for(int j = 1;j <= n;j++){
				sum = sum + abs(i - j);
			}
		}
		int s = 0; int t = n + 1;
		for(int i = 1;i <= n;i++){
			for(int j = i + 1;j <= n;j++){
				add(i,j,j - i);
				if(vis[i][j]){
					add(i,t,j - i);
					add(j,t,j - i);
				}
				else{
					add(s,i,j - i);
					add(s,j,j - i);
				}
			}
		}
		printf("%lld\n",(sum - dinic(s,t)) / 2);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值