UVA - 1389 Hard Life【分数规划+最小割】【最大权闭合图】

Time limit 3000 ms
在这里插入图片描述


题目大意

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


题目分析

看到这种比率的形式马上想到分数规划
即求原图的一个子图 G ′ = ( V ′ , E ′ ) G^{'}=(V^{'},E^{'}) G=(V,E)使得 ∑ e ∈ E ′ 1 ∑ v ∈ V ′ 1 \frac{\sum_{e\in E^{'}}1}{\sum_{v\in V^{'}}1} vV1eE1最大化
那么按照分数规划的套路二分比值(设为g)
判断是否存在方案使得 ∑ e ∈ E ′ 1 ∑ v ∈ V ′ 1 ≥ g ⇒ ∑ e ∈ E ′ 1 − ∑ v ∈ V ′ g ≥ 0 \frac{\sum_{e\in E^{'}}1}{\sum_{v\in V^{'}}1}\geq g \Rightarrow \sum_{e\in E^{'}}1-\sum_{v\in V^{'}}g\geq 0 vV1eE1geE1vVg0成立

根据题意可知选择一条边则必定选择这条边的两个端点
那么上面这个式子恰好对应到了最大权闭合子图的模型

源点s向每对矛盾连边,容量为1
每个员工向汇点t连边,容量为g
每个矛盾向其对应的员工连边,容量为inf
判断最大收益(矛盾数m-最小割)是否大于等于0即可

被精度活活卡死

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long lt;
typedef double dd;
#define eps 1e-8

int read()
{
    int x=0,f=1;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

const dd inf=1e9;
const int maxn=10010;
int n,m;
struct node{int v,nxt;dd f;}E[maxn<<2];
int head[maxn],tot=1;
int lev[maxn];
struct edge{int u,v;}edge[maxn];
int judge[110];
vector<int> rem;

void add(int u,int v,dd f)
{
    E[++tot].nxt=head[u];
    E[tot].v=v; E[tot].f=f;
    head[u]=tot;
    
    E[++tot].nxt=head[v];
    E[tot].v=u; E[tot].f=0.0;
    head[v]=tot;
}

int bfs(int s,int t)
{
    memset(lev,-1,sizeof(lev)); lev[s]=0;
    queue<int> q; q.push(s);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=E[i].nxt)
        {
            int v=E[i].v;
            if(lev[v]==-1&&E[i].f>0)
            {
                lev[v]=lev[u]+1;
                if(v==t) return 1;
                q.push(v);
            }
        }
    }
    return 0;
}

dd dfs(int u,dd cap,int t)
{
    if(u==t) return cap;
    dd flow=cap;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(lev[v]==lev[u]+1&&E[i].f>0&&flow>0)
        {
            dd f=dfs(v,min(E[i].f,flow),t);
            E[i].f-=f; E[i^1].f+=f;
            flow-=f;
        }
    }
    return cap-flow;
}

dd dicnic(int s,int t)
{
    dd maxf=0;
    while(bfs(s,t)) maxf+=dfs(s,inf,t);
    return maxf;
}

int calc(dd x)
{
    memset(head,0,sizeof(head)); tot=1;	
    int s=n+m+1,t=s+1;
    for(int i=1;i<=m;++i)
    {
        add(s,i,1.0);
        add(i,edge[i].u+m,inf);
        add(i,edge[i].v+m,inf);
    }
    for(int i=1;i<=n;++i)
    add(i+m,t,x);
    
    dd maxf=dicnic(s,t);
    return 1.0*m-maxf;
}

int solve(dd rat)
{
    rem.clear();
    memset(judge,0,sizeof(judge));
    
    int s=n+m+1,t=s+1,res=0;
    calc(rat); bfs(s,t);
    for(int i=1;i<=m;++i)
    if(lev[i]!=-1) 
    {
        if(++judge[edge[i].u]==1) res++;
        if(++judge[edge[i].v]==1) res++;
    }
    for(int i=1;i<=n;++i)
    if(judge[i]) rem.push_back(i);
    return res;
}

int main()
{
    int cs=0;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
    	if(cs++!=0) printf("\n");
        if(m==0){ printf("1\n1\n"); continue;}
        for(int i=1;i<=m;++i)
    	edge[i].u=read(),edge[i].v=read();
    
    	dd L=1.0/(dd)n,R=m*1.0,rat=0;
    	while(R-L>eps)
    	{
    		dd mid=(L+R)/2.0;
    		if(calc(mid)>eps) rat=L=mid;
    		else R=mid;
        }
        
        int ans=solve(rat);
        printf("%d\n",ans);
        for(int i=0;i<rem.size();++i)
        printf("%d\n",rem[i]);
    }
    return 0;
}

集训队论文–胡伯涛《最小割模型在信息学竞赛中的应用》里面还描述了一种进一步优化的方法
但蒟蒻还没完全理解,过段时间再补上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值