UVALive 3231 Fair Share 二分+网络流

https://vjudge.net/problem/UVALive-3231
在这里插入图片描述题目大意:每组数据 n n 个处理器, m m 个任务,每个任务限定两个处理器,只能使用其中的某一个来解决,每个任务都需要花费 1 1 分钟来解决,你需要在解决所有任务的情况下使得花费时间最长的那个处理器所花费的时间尽可能短。

思路:首先考虑最大流建图,源点向每个任务连边权为 1 1 的边,每个任务向它的两个处理器连边权为 1 1 的边,然后二分设中间值为 m i d mid ,每个处理器向汇点连边权为 m i d mid 的边,最大流即为解决的任务数量,和 m m 进行判断修改上下界就行了。注意每次二分都要修改边权。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=2e4+6;
const int maxm=4e4+5;

struct Edge
{
	int to,nxt,f;
};      //前向星存边
Edge edge[maxm<<1]; //边的空间至少要开两倍 因为有反向边的存在
int head[maxn],cur[maxn];//cur用于当前弧优化
int tot=1; //边的编号
int dep[maxn],cnt[maxn];
int n,m,s,t; //定点数 边数 源点 汇点

inline void addedge(int u,int v,int dis) //加一条 u->v 容量为dis的边 那么自然要加一条 v->u 流量为0的反向边
{
	edge[++tot].to=v,edge[tot].f=dis;
	edge[tot].nxt=head[u];
	head[u]=tot;
	edge[++tot].to=u,edge[tot].f=0;
	edge[tot].nxt=head[v];
	head[v]=tot;
}

void bfs()
{
    memset(dep,-1,sizeof(dep));
    memset(cnt,0,sizeof(cnt));
    dep[t]=0,cnt[0]=1;
    queue<int> q;
    q.push(t);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(!dep[v])
            {
                dep[v]=dep[u]+1;
                ++cnt[dep[v]];
                q.push(v);
            }
        }
    }
}

int dfs(int u,int lim)
{
    if(u==t)
        return lim;
    int ans=0;
    for(int i=head[u];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(edge[i].f&&dep[u]==dep[v]+1)
        {
            int flow=dfs(v,min(lim,edge[i].f));
            edge[i].f-=flow;
            edge[i^1].f+=flow;
            lim-=flow;
            ans+=flow;
            if(!lim)
                break;
        }
    }
    if(lim)
    {
        if(--cnt[dep[u]]==0)
            dep[s]=t+1;
        ++cnt[++dep[u]];
    }
    return ans;
}

int isap()
{
    int ans=0;
    bfs();
    while(dep[s]<=t)
    {
        memcpy(cur,head,sizeof(cur));
        ans+=dfs(s,INF);
    }
    return ans;
}

int main()
{
    int tt;
    scanf("%d",&tt);
	while(tt--)
	{
	    scanf("%d%d",&n,&m);
		memset(head,0,sizeof(head));
		tot=1;
		int u,v;
		s=0,t=n+m+1;
		for(int i=1;i<=m;i++) //源点向每个任务连权值为1的边
            addedge(s,i,1);
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			addedge(i,m+u,1);//每个任务向它的两个处理器连权值为1的边
			addedge(i,m+v,1);
		}
		int l=1,r=m,mid=l+r>>1; //二分答案
		int last=tot+1; //记录下一条边的编号 方便以后更新
		for(int i=1;i<=n;i++) //每个处理器向汇点连权为 mid 的边
            addedge(m+i,t,mid);
        int ans;
        while(l<=r) //求最小值
        {
            mid=l+r>>1;
            for(int i=2;i<last;i+=2) //修改边权
                edge[i].f=1,edge[i^1].f=0;
            for(int i=last;i<=tot;i+=2)
                edge[i].f=mid,edge[i^1].f=0;
            ans=isap();
            if(ans<m)//不满足题意 说明每个处理器最多接受的任务数过少
                l=mid+1;
            else
                r=mid-1;
        }
	printf("%d\n",l);
	}
	return 0;
}
发布了677 篇原创文章 · 获赞 30 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/104683552
今日推荐