POJ--3686[The Windy's] 最小费用流或KM

47 篇文章 1 订阅
45 篇文章 0 订阅

题意:
有N个任务和M个机器,给出第i个任务在第j个机器完成的时间map[i][j],每台机器同一时刻只能处理一个任务,机器必须完整地完成一个任务后才能接着完成下一个任务。问N个任务完成时间的平均值最少为多少。


关键:
设N个任务的执行时间分别为T1,T2…TN,则N个订单的总的执行时间是
T1*N + T2*(N-1) + … + TN-1*2 + TN。


构图:
将每台机器拆成N个点。第k个点表示倒数第k个完成的任务。将任务i和机器j的第k个分点连权值为map[i][j]*k的边。

总点数:N+M*N+2;

总边数:(N+N*M+N*N*M)*2;


PS.这题是比较好的构图题。虽然费用流效率比较低可是目前还没看过KM,只好用费用流了。



构图代码:

void Init()//构图
{
	int i,j,k;
	memset(head,-1,sizeof(head));ecnt=0;
	scanf("%d%d",&N,&M);
	scr=0;sink=N+M*N+1;vn=sink+1;
	for(i=1;i<=N;i++)
		Insert(scr,i,1,0);
	for(i=N+1;i<=M*N+N;i++)
		Insert(i,sink,1,0);
	for(i=1;i<=N;i++)
	{
		for(j=1;j<=M;j++)
		{
			scanf("%d",&map[i][j]);
			//Insert(i,j+N,1,map[i][j]);
		}
	}
	for(i=1;i<=N;i++)
	{
		int cnt=N+1;
		for(j=1;j<=M;j++)
		{
			for(k=1;k<=N;k++)
			{
				Insert(i,cnt++,1,map[i][j]*k);
			}
		}
	}
}



CODE:

/*最小费用流+拆点*/
/*AC代码:969ms*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#include <queue>
#define MAXN 2600
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define INF 1e8
using namespace std;
struct edge
{
	int u,v,w,c,next;
}E[800000];
int head[MAXN],ecnt;
bool vis[MAXN];
int dis[MAXN],pre[MAXN];
int map[55][55];
int N,M,scr,sink,vn;

void Insert(int u,int v,int w,int c)
{
	E[ecnt].u=u;
	E[ecnt].v=v;
	E[ecnt].w=w;
	E[ecnt].c=c;
	E[ecnt].next=head[u];
	head[u]=ecnt++;
	E[ecnt].u=v;
	E[ecnt].v=u;
	E[ecnt].w=0;
	E[ecnt].c=-c;
	E[ecnt].next=head[v];
	head[v]=ecnt++;
}
void Init()//构图
{
	int i,j,k;
	memset(head,-1,sizeof(head));ecnt=0;
	scanf("%d%d",&N,&M);
	scr=0;sink=N+M*N+1;vn=sink+1;
	for(i=1;i<=N;i++)
		Insert(scr,i,1,0);
	for(i=N+1;i<=M*N+N;i++)
		Insert(i,sink,1,0);
	for(i=1;i<=N;i++)
	{
		for(j=1;j<=M;j++)
		{
			scanf("%d",&map[i][j]);
			//Insert(i,j+N,1,map[i][j]);
		}
	}
	for(i=1;i<=N;i++)
	{
		int cnt=N+1;
		for(j=1;j<=M;j++)
		{
			for(k=1;k<=N;k++)
			{
				Insert(i,cnt++,1,map[i][j]*k);
			}
		}
	}
}

queue<int>Q;
bool SPFA(int s,int t,int n)
{
	
	int i,u,v,c;
	memset(vis,false,sizeof(vis));
	for(i=0;i<=n;i++)
		dis[i]=INF;
	Q.push(s);
	pre[s]=-1;
	dis[s]=0;
	vis[s]=true;
	while(!Q.empty())
	{
		//printf("^\n");
		u=Q.front();Q.pop();
		vis[u]=false;
		for(i=head[u];i!=-1;i=E[i].next)
		{
			v=E[i].v;c=E[i].c;
			if(E[i].w>0&&dis[v]>dis[u]+c)//前提是E[i].w>0
			{
				dis[v]=dis[u]+c;
				pre[v]=i;
				if(!vis[v])
				{
					vis[v]=true;
					Q.push(v);
				}
			}
		}
	}
	if(dis[t]<INF) return true;
	return false;
}

void Solve()
{
	int i,u,v,du,dv,ans=0,flow;
	while(SPFA(scr,sink,vn))
	{
		//printf("*\n");
		ans+=dis[sink];
		for(i=pre[sink];i!=-1;i=pre[E[i].u])//更新容量
		{
			E[i].w--;
			E[i^1].w++;
		}
	}
	double res=ans*1.0/N;
	printf("%.6lf\n",res);
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		Init();
		Solve();
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__简言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值