【BZOJ3442】学习小组 费用流

#include <stdio.h>
int main()
{
	puts("转载请注明出处谢谢");
	puts("http://blog.csdn.net/vmurder/article/details/43057197");
}


题意:那个要尽量多的学生参加的意思就是一个学生如果对至少一个小组有兴趣,就一定要让他参加至少一个小组,然后每个学生至多参加k个,但是不是强迫参加。


题解:费用流


算了,还是说说建图吧。

模式:add(入点,出点,流量,费用)(别忘了反向弧)

add(源点,学生,k,0),add(学生,汇点,k-1,0)

对于每个学生A有兴趣的小组B add(A,B+n,1,0)

连n条边,第j条为 add(小组,汇点,C(2j-1)-F)


有些思想写了太水浪费时间,不写怕初学者看不懂%

算了不写了,反正博客都是给自己看的。

看不懂的话留言吧。至少这几个月都是天天上blog的。


代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200
#define M 50000
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
using namespace std;
struct KSD
{
	int u,v,len,fee,next;
}e[M];
int head[N],cnt;
inline void add(int u,int v,int len,int fee)
{
	e[++cnt].u=u;
	e[cnt].v=v;
	e[cnt].len=len;
	e[cnt].fee=fee;
	e[cnt].next=head[u];
	head[u]=cnt;
}
int dist[N],s,t;
int lim[N],pre[N];
bool in[N];
queue<int>q;
void spfa()
{
	while(!q.empty())q.pop();
	memset(dist,0x3f,sizeof dist);

	q.push(s),dist[s]=0,in[s]=1;
	int i,u,v;
	while(!q.empty())
	{
		u=q.front(),q.pop(),in[u]=0;
		for(i=head[u];i;i=e[i].next)if(e[i].len)
		{
			if(dist[v=e[i].v]>dist[u]+e[i].fee)
			{
				dist[v]=dist[u]+e[i].fee;
				lim[v]=min(e[i].len,lim[u]);
				pre[v]=i;
				if(!in[v])q.push(v),in[v]=1;
			}
		}
	}
	return ;
}
void handle(int flow)
{
	for(int i=pre[t];i;i=pre[e[i].u])
	{
		e[i].len-=flow;
		e[i^1].len+=flow;
	}
}
int minfee,n,m,p;
int C[N],F[N];
char str[N];
void build()
{
	int i,j,k;
	scanf("%d%d%d",&n,&m,&p);
	s=n+m+1,t=n+m+2;
	cnt=1,lim[s]=inf;

	for(i=1;i<=m;i++)scanf("%d",&C[i]);
	for(i=1;i<=m;i++)scanf("%d",&F[i]);

	for(i=1;i<=n;i++)
	{
		add(s,i,p,0),add(i,s,0,0); // 每个学生可以选择小组的数量
		if(p>1)add(i,t,p-1,0),add(t,i,0,0); // 每个学生可以放弃选择小组的次数
	}
	for(i=1;i<=m;i++)
	{
		for(j=1;j<=n;j++)
		{
			int Fee=C[i]*(2*j-1)-F[i];
			add(i+n,t,1,Fee),add(t,i+n,0,-Fee);
		}
	}

	for(i=1;i<=n;i++)
	{
		scanf("%s",str+1);
		for(j=1;j<=m;j++)if(str[j]=='1')
		{
			add(i,j+n,1,0);
			add(j+n,i,0,0);
		}
	}
	return ;
}
int main()
{
	freopen("test.in","r",stdin);
	build();
	while(spfa(),dist[t]<inf)
	{
		minfee+=lim[t]*dist[t];
		handle(lim[t]);
	}
	cout<<minfee<<endl;
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值