zoukankan      html  css  js  c++  java
  • 【POJ3155】生活的艰辛Hard Life

    题面

    Description

    ADN公司内部共 n个员工,员工之间可能曾经因为小事有了过节,总是闹矛盾。若员工u和员工 v有矛盾,用边(u, v)表示,共 m个矛盾。最近,ADN公司内部越来越不团结,Amber决定裁员。Amber想得到一个被裁人员的清单,使得被裁人员间的不团结率最高。不团结率定义为被裁人员间的矛盾总数与被裁人员数的比值(不团结率=被裁人员之间的矛盾总数/被裁人员数)。

    img

    在上图这个例子中1, 2, 4和5,4个人中都有5对矛盾,则不团结率为(frac 45)。如果我们添加3到这个团队,则不团结率就下降到(frac 56)

    Input

      输入文件的第一行包含两个整数n和m (1≤n≤100,0≤m≤1000),n表示公司的总人数(编号从1到n),m表示矛盾的对数。
      接下来m行,每行两个整数ai和bi(1≤ai,bi≤n,ai≠bi),描述一对矛盾,每对矛盾只会出现一次。

    Output

      输出文件的第一行为一个整数k(1≤k≤n),表示最不团结的团队总人数。

    Sample Input

    sample input #1
    5 6
    1 5
    5 4
    4 2
    2 5
    1 2
    3 1

    sample input #2
    4 0

    Sample Output

    sample output #1
    4

    sample output #2
    1

    Hint

    Note, that in the last example any team has hardness factor of zero, and any non-empty list of people is a valid answer.

    题目分析

    最大密度子图模板题。

    假设答案为(k) ,则要求解的问题是:

    选出一个合适的点集 (V) 和边集 (E),令((|E|−k∗|V|))取得最大值。

    所谓“合适”是指满足如下限制:若选择某条边,则必选择其两端点。

    建图:

    以原图的边作为左侧顶点,权值为(1)

    原图的点作为右侧顶点,权值为(+k)

    若原图中存在边 ((u,v)),则新图中添加两条边 (([uv]−>u),([uv]−>v)),转换为最大权闭合子图

    代码实现

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #include<iomanip>
    #include<cstdlib>
    #define MAXN 0x7fffffff
    #define eps 1e-9
    typedef long long LL;
    const int N=1105;
    using namespace std;
    inline int Getint(){register int x=0,f=1;register char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}return x*f;}
    int n,m,S,T,num;
    struct node{int next,to,pair;double flow;}g[N<<3];
    struct Edge{int x,y;}s[N];
    int h[N],cnt;
    void AddEdge(int x,int y,double z){
    	g[++cnt].to=y,g[cnt].next=h[x],h[x]=cnt,g[cnt].flow=z,g[cnt].pair=cnt+1;
    	g[++cnt].to=x,g[cnt].next=h[y],h[y]=cnt,g[cnt].flow=0,g[cnt].pair=cnt-1;
    }
    int GAP[N],dis[N];
    void Init(){
    	static int q[N];
    	fill(dis,dis+num,0),fill(GAP,GAP+num,0);
    	int l=0,r=1;q[++l]=T,++GAP[dis[T]=1];
    	while(l<=r){
    		int x=q[l++];
    		for(int i=h[x];i;i=g[i].next){
    			int to=g[i].to;
    			if(!dis[to])++GAP[dis[to]=dis[x]+1],q[++r]=to; 
    		}
    	}
    }
    double Dfs(int x,double Maxf){
    	if(x==T||!Maxf)return Maxf;
    	double ret=0;
    	for(int i=h[x];i;i=g[i].next){
    		int to=g[i].to;
    		if(g[i].flow&&dis[x]==dis[to]+1){
    			double dlt=Dfs(to,min(g[i].flow,Maxf-ret));
    			g[i].flow-=dlt;
    			g[g[i].pair].flow+=dlt;
    			ret+=dlt;
    			if(dis[S]==num+1||ret+eps>=Maxf)return ret;
    		}
    	}
    	if(!(--GAP[dis[x]]))dis[S]=num+1;
    	else GAP[++dis[x]]++;
    	return ret;
    }
    double SAP(){
    	Init();
    	double ans=Dfs(S,MAXN);
    	while(dis[S]<=num)ans+=Dfs(S,MAXN);
    	return ans;
    }
    bool Check(double mid){
    	fill(h,h+num,0),cnt=0;
    	for(int i=1;i<=m;i++)AddEdge(S,i,1),AddEdge(i,s[i].x+m,MAXN),AddEdge(i,s[i].y+m,MAXN);
    	for(int i=1;i<=n;i++)AddEdge(i+m,T,mid);
    	return m-SAP()>0;
    } 
    int ans;bool vis[N];
    void Find(int x){
    	ans+=(x>m&&x<=m+n);
    	vis[x]=1;
    	for(int i=h[x];i;i=g[i].next){
    		int to=g[i].to;
    		if(g[i].flow&&!vis[to])Find(to);
    	}
    }
    int main(){
    	n=Getint(),m=Getint(),S=0,T=n+m+1,num=T+1;
    	if(!m)cout<<1,exit(0);
    	for(int i=1;i<=m;i++)s[i].x=Getint(),s[i].y=Getint();
    	double l=0,r=m,Eps=1.0/n/n;
    	while(l+Eps<r){
    		double mid=(l+r)/2;
    		if(Check(mid))l=mid;
    		else r=mid;
    	}
    	Check(l),Find(S);
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    【Anagrams】 cpp
    【Count and Say】cpp
    【Roman To Integer】cpp
    【Integer To Roman】cpp
    【Valid Number】cpp
    重构之 实体与引用 逻辑实体 逻辑存在的形式 可引用逻辑实体 不可引用逻辑实体 散弹式修改
    Maven项目聚合 jar包锁定 依赖传递 私服
    Oracle学习2 视图 索引 sql编程 游标 存储过程 存储函数 触发器
    mysql案例~tcpdump的使用
    tidb架构~本地化安装
  • 原文地址:https://www.cnblogs.com/Emiya-wjk/p/10069179.html
Copyright © 2011-2022 走看看