1. 程式人生 > >【POJ3155】生活的艱辛Hard Life

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