BZOJ 3442 学习小组 费用流

题目大意:给出学生的数目和学习小组的数目,学生参加小组需要交纳费用,每个小组会支出C[i]*cnt[i]^2。每个学生可以参加k个小组,问最多的学生参加时,最小支出费用。

思路:如果不算后面那个什么鬼的条件的话,见图十分显然。

S->每个学生 f:k,c:0

每个学生->每个学习小组 f:1,c:-F[i]

每个学习小组->T f:1,c:1 * C[i],3 * C[i],5 * C[i],7 * C[i],......

后面的条件其实是说,每个学生的k次机会不一定全用光,但是所有人都要参加至少一个小组。于是我们可以每个人->T f:k - 1,c:0

然后跑费用流

CODE:

#define _CRT_SECURE_NO_DEPRECATE

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 510
#define MAXE 200010
#define INF 0x3f3f3f3f
#define S 0
#define T (MAX - 1)
using namespace std;

 struct MinCostMaxFlow{
	int head[MAX],total;
	int next[MAXE],aim[MAXE],flow[MAXE],cost[MAXE];
	int from[MAX],p[MAX];

	int f[MAX];
	bool v[MAX];

	MinCostMaxFlow() {
		total = 1;
	}
	void Add(int x,int y,int f,int c) {
		next[++total] = head[x];
		aim[total] = y;
		flow[total] = f;
		cost[total] = c;
		head[x] = total;
	}
	void Insert(int x,int y,int f,int c) {
		Add(x,y,f,c);
		Add(y,x,0,-c);
	}
	bool SPFA() {
		static queue<int> q;
		while(!q.empty())	q.pop();
		memset(f,0x3f,sizeof(f));
		memset(v,false,sizeof(v));
		f[S] = 0;
		q.push(S);
		while(!q.empty()) {
			int x = q.front(); q.pop();
			v[x] = false;
			for(int i = head[x]; i; i = next[i])
				if(flow[i] && f[aim[i]] > f[x] + cost[i]) {
					f[aim[i]] = f[x] + cost[i];
					if(!v[aim[i]])
						v[aim[i]] = true,q.push(aim[i]);
					from[aim[i]] = x;
					p[aim[i]] = i;
				}
		}
		return f[T] != INF;
	}
	int EdmondsKarp() {
		int re = 0;
		while(SPFA()) {
			int max_flow = INF;
			for(int i = T; i != S; i = from[i])
				max_flow = min(max_flow,flow[p[i]]);
			for(int i = T; i != S; i = from[i]) {
				flow[p[i]] -= max_flow;
				flow[p[i]^1] += max_flow;
			}
			re += f[T] * max_flow;
		}
		return re;
	}
}solver;

int points,groups,k;
int C[MAX],F[MAX];
bool map[MAX][MAX];

int main()
{
	cin >> points >> groups >> k;
	for(int i = 1; i <= groups; ++i)	scanf("%d",&C[i]);
	for(int i = 1; i <= groups; ++i)	scanf("%d",&F[i]);
	for(int i = 1; i <= points; ++i)
		for(int j = 1;j <= groups; ++j)
			scanf("%1d",&map[i][j]);
	for(int i = 1; i <= points; ++i) {
		solver.Insert(S,i,k,0);
		for(int j = 1; j <= groups; ++j)
			if(map[i][j])
				solver.Insert(i,points + j,1,-F[j]);
	}
	if(k - 1)
		for(int i = 1; i <= points; ++i)
			solver.Insert(i,T,k - 1,0);
	for(int i = 1; i <= groups; ++i)
		for(int j = 1; j <= points; ++j)
			solver.Insert(i + points,T,1,((j << 1) - 1) * C[i]);
	cout << solver.EdmondsKarp() << endl;
	return 0;
}

时间: 2024-10-11 23:02:15

BZOJ 3442 学习小组 费用流的相关文章

【bzoj3442】学习小组 费用流

原文地址:http://www.cnblogs.com/GXZlegend/p/6809670.html 题目描述 [背景] 坑校准备鼓励学生参加学习小组. [描述] 共有n个学生,m个学习小组,每个学生有一定的喜好,只愿意参加其中的一些学习小组,但是校领导为学生考虑,规定一个学生最多参加k个学习小组.财务处的大叔就没那么好了,他想尽量多收钱,因为每个学生参加学习小组都要交一定的手续费,不同的学习小组有不同的手续费.然而,事与愿违,校领导又决定对学习小组组织者进行奖励,若有a个学生参加第i个学习

BZOJ 2661 连连看(费用流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2661 题意:给出一个区间[a,b]中的全部整数,如果其中某两个数x,y(设x>y)的平方差x^2-y^2是一个完全平方数z^2,并且y与z互质,那么就可以将x和y一起消除,同时得到x+y点分数.要求就是,消除的数对尽可能多的前提下,得到的分数尽量多. 思路:首先暴力出所有合法的数对(x,y).然后将每个用到的数字拆成两个点,每个数对连一条边.最后的答案除以2即可. struct nod

BZOJ 2879 美食节(费用流-动态加边)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2879 题意:有n道菜,每道菜需要b[i]份,m个厨师,第j个厨师做第i道菜需要时间a[i][j],求做完所有菜,所有人等待的最小总时间. 思路:设所有的菜为sum.一个明显的思路是将每个厨师拆成sum个点.然后sum个菜每个菜向每个厨师的每个点连边,表示该道菜为该厨师第几个做.由于这样数据太大.动态加边.每次增光一次后找到此次增广的厨师,每道菜将其连边. struct node { i

BZOJ 2424: [HAOI2010]订货 费用流

2424: [HAOI2010]订货 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2424 Description 某公司估计市场在第i个月对某产品的需求量为Ui,已知在第i月该产品的订货单价为di,上个月月底未销完的单位产品要付存贮费用m,假定第一月月初的库存量为零,第n月月底的库存量也为零,问如何安排这n个月订购计划,才能使成本最低?每月月初订购,订购后

BZOJ 2597 WC2007 剪刀石头布 费用流

题目大意:给出一张竞赛图中的其中几条单向边,剩下的边随意定向.问最多可以形成多少三元环. 思路:对于任意三个点来说,他们组成了三元环,当且仅当这些点的入度=处度 = 1.如果没有组成三元环,只需要改变这其中任意一条边的方向,使得一个点的入度变成2,一个点的出度变成2.我们只需要算出有多少三个点中有一个点的入度为2的就可以了,并最小化这个东西. 通过公式:ans=C(n,3)-ΣC(degree[x],2)可以发现,我们只需要让每个点的入度尽可能小.由此想到费用流模型(我怎么想不到..) 类似于x

BZOJ 1927 星际竞速(费用流)

考虑费用流,题目要求走n个点都走完且恰好一次,显然流量的限制为n. 建立源点s和汇点t,并把每个星球拆成两个点i和i',分别表示已到达该点和经过该点. 对于能力爆发,建边(s,i',1,w). 对应高速航行,建边(s,i,1,0), (i,j',1,w). 因为每个点必须走一次且只能走一次.建边(i',t,1,0). 其实就是类似最小路径覆盖的建图方法. # include <cstdio> # include <cstring> # include <cstdlib>

BZOJ SCOI 2007 修车 费用流

题目大意:有一些车和一些修车的人,给出每个人修每个车的时间,问所有人等待的最短平均时间是多少. 思路:记得POJ有一个和这个很像的题,做法是一样的.对于每个人修车的时候,我们只考虑他修车的时间对在它之后修车的人的时间的影响,因此我们只要考虑每一辆车是倒数第几个修的就可以了,然后朴素的建图,跑朴素的费用流,就可以过. CODE: #include <queue> #include <cstdio> #include <cstring> #include <ioman

算法学习:费用流

[前置知识] 最大流 [问题描述] 给定一个图,图上的边每条边有流量,流量有费用, 求在流量为 F 时费用的最小值 [分析思路] 求费用最小,我们很容易想到求最短路径,我们只需要将费用看作代价然后求最短路不久能求出来最小费用了嘛 但是,问题来了 我们又如何能够在求最小费用的同时,满足流量最终为F的条件 在最大流中提到,每次增广都是在残余网络中通过询问 s 至 t 的残余网络进行增广 所以,我们每次在残余网络上求最短路就可以了 这里注意,残余网络中的反向边的费用应该时原边费用的相反数 这样在计算的

BZOJ 2879 NOI2012 美食节 费用流

题目大意:给定n道菜和m个厨师,第i道菜需要p[i]份,第j个厨师做第i道菜需要时间t[i][j],求最长总等待时间 一个厨师做的倒数第一道菜对答案的贡献是时间的一倍,倒数第二道菜对答案的贡献是时间的两倍,以此类推 厨师们怒了!发动符卡·禁忌『p重存在』! 将每个厨师拆成Σp[i]个点,每道菜向每个厨师的第i个点连一条流量为1,费用为时间的i倍,每个点向汇点连一条流量为1费用为0的边,跑最小费用最大流 这能跑?不会T到死? 动态加点即可 每个厨师初始只有一个点,如果这个厨师做完了最后一道菜,就添