zoukankan      html  css  js  c++  java
  • 学习小组 洛谷P4209

    原题

    这是一道比较优秀的技巧类题目。

    我们通过观察数据范围,发现很小,首先排除贪心。

    由于题目限制多而复杂,我们考虑使用网络流来描述这个问题。

    考虑建立一个网络流框架,大概是我们要求每个能选小组的学生都至少选一个小组,可以使用最大流描述,但是题目有要求支出最少,因此考虑使用费用流进一步描述。

    先考虑如何描述参加了一些学生的小组的支出,我们可以发现由于支出增长是平方倍级别的,因此无法直接用同一的费用描述。我们发现由于 (C,F) 是正数,因此不难证明有以下式子:

    [C_i imes (a+1)^2 - F_i imes (a+1) - C_i imes a^2 + F_i imes a > C_i imes a^2 - F_i imes a - C_i imes (a-1)^2 + F_i imes (a-1) ]

    即相邻参加人数增加所带来的花费变化量是单调递增的,我们考虑把他的花费进行差分,拆成 (n) 条边与汇点相连,根据贪心想法,跑费用流时一定会优先选较小的边,因此正确性可以保证。

    另外,考虑如何限制每个学生至少选择一个。根据贪心想法,由于询问最少支出,我们考虑在什么时候学生会选择不止一个学习小组。显然,当且仅当多选择小组会让花费更小时我们才会多选,为了保证学生至少选一个我们考虑使用满流的方式限制。因此我们可以从每个学生往汇点连接一条容量为 (k - 1) ,花费为 (0) 的边,就可以限制这个学生至少选择一个小组,同时想要多选则花费必须比 (0) 更小。

    至此,我们的建图满足了题目的所有限制条件,代码如下:

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    
    template <typename T>
    void read(T &x) {
    	T f=1;x=0;char s=getchar();
    	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
    	x *= f;
    }
    
    const int MAXN = 1e5 + 5;
    const int MAXM = 1e5 + 5;
    const LL inf = 1e15;
    
    int head[MAXN] , to[MAXM << 1] , nxt[MAXM << 1] , cnt = 1;
    LL edge[MAXM << 1] , val[MAXM << 1];
    void add(int u , int v , LL c , LL w) {
    	nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;edge[cnt] = c;val[cnt] = w;
    	nxt[++cnt] = head[v];head[v] = cnt;to[cnt] = u;edge[cnt] = 0;val[cnt] = -w;
    }
    
    int las[MAXN] , pre[MAXN] , num , s , t , vis[MAXN];
    LL dis[MAXN] , flow[MAXN];
    
    struct MinCostMaxFlow {
    	LL MaxFlow , MinCost;
    	bool bfs() {
    		for (int i = 1; i <= num; ++i) las[i] = 0 , dis[i] = inf , pre[i] = 0 , flow[i] = 0 , vis[i] = 0;
    		flow[s] = inf , dis[s] = 0;
    		queue <int> q;q.push(s);
    		int flag = 0;
    		while(!q.empty()) {
    			int x = q.front();
    			q.pop();
    			vis[x] = 0;
    			for (int i = head[x]; i; i = nxt[i]) {
    				if(!edge[i]) continue;
    				int v = to[i];
    				if(dis[v] > dis[x] + val[i]) {
    					dis[v] = dis[x] + val[i];
    					flow[v] = min(flow[x] , edge[i]);
    					pre[v] = x;
    					las[v] = i;
    					if(v == t) {
    						flag = 1;
    						continue;
    					}
    					if(!vis[v]) vis[v] = 1 , q.push(v);
    				}
    			}
    		} 
    		return flag;
    	}
    	void MVMC() {
    		MaxFlow = 0 , MinCost = 0;
    		while(bfs()) {
    			int now = t;
    			MaxFlow += flow[t];
    			MinCost += dis[t] * flow[t];
    			while(now != s) {
    				edge[las[now]] -= flow[t];
    				edge[las[now] ^ 1] += flow[t];
    				now = pre[now];
    			}
    		}
    	} 
    }MIN;
    
    int n , m , k , id[MAXN] , C[MAXN];
    int per[MAXN];
    
    int main() {
    	read(n),read(m),read(k);
    	s = 1 , t = 2 , num = 2;
    	for (int i = 1; i <= m; ++i) {
    		read(C[i]);
    		id[i] = ++num;
    	}
    	for (int i = 1; i <= m; ++i) {
    		int F;
    		read(F);
    		for (int j = 1; j <= n; ++j) add(id[i] , t , 1 , C[i] * (2 * j - 1) - F);
    	}
    	for (int i = 1; i <= n; ++i) {
    		per[i] = ++num;
    		char s[105];
    		scanf("%s" , s + 1);
    		for (int j = 1; j <= m; ++j) {
    			if(s[j] == '1') add(per[i] , id[j] , 1 , 0);
    		}
    	}
    	for (int i = 1; i <= n; ++i) {
    		add(s , per[i] , k , 0);
    		add(per[i] , t , k - 1 , 0);
    	}
    	MIN.MVMC();
    	printf("%lld" , MIN.MinCost);
    	return 0;
    }
    
  • 相关阅读:
    检测到有潜在危险的 Request.Form 值
    检查用户是否有权限
    尝试用户自动登录
    用反射封装HttpHandler,实现通过action方法名调用方法
    mac 系统下安装MYSQL数据库
    iOS 下配置XMPP 服务器openfire详解
    【2014最新】iOS App 提交上架store 详细流程(转)
    面试题
    iOS开发之如何在xcode中自定义快捷键
    AFNetwork 作用和用法详解
  • 原文地址:https://www.cnblogs.com/Reanap/p/14238156.html
Copyright © 2011-2022 走看看