标签:font 地址 log return amp zoj ems cpp style
原文地址:http://www.cnblogs.com/GXZlegend/p/6809670.html
题目描述
输入
输出
样例输入
3 3 1
1 2 3
3 2 1
111
111
111
样例输出
-2
题解
拆边+费用流
由于有Ci*a^2的存在,使得正常加边的费用流无法处理。我们可以加容量为1,费用为Ci*1、Ci*3、Ci*5、Ci*7、...的一堆边,这样在最小费用的前提下总花费满足题意。
每个学习小组向T连上述所说的边,S向每个学生连一条容量为k,费用为0的边,每个学生向其能参加的学习小组连一条容量为1,费用为Fi的边。
因为题目中描述“在参与学生(而不是每个学习小组的人数总和)尽量多的情况下”,指的是所有学生必须有流通过,但不必满流。
所以还要从每个学生向T连一条容量为k-1,费用为0的边,保证费用最小。
然后跑最小费用最大流即可。
#include <cstdio> #include <cstring> #include <queue> using namespace std; queue<int> q; int f[110] , head[210] , to[100000] , val[100000] , cost[100000] , next[100000] , cnt = 1 , s , t , dis[210] , from[210] , pre[210]; char str[110]; void add(int x , int y , int v , int c) { to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt; } bool spfa() { int x , i; memset(dis , 0x3f , sizeof(dis)); memset(from , -1 , sizeof(from)); dis[s] = 0 , q.push(s); while(!q.empty()) { x = q.front() , q.pop(); for(i = head[x] ; i ; i = next[i]) if(val[i] && dis[to[i]] > dis[x] + cost[i]) dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i , q.push(to[i]); } return ~from[t]; } int mincost() { int i , k , ans = 0; while(spfa()) { k = 0x3f3f3f3f; for(i = t ; i != s ; i = from[i]) k = min(k , val[pre[i]]); ans += k * dis[t]; for(i = t ; i != s ; i = from[i]) val[pre[i]] -= k , val[pre[i] ^ 1] += k; } return ans; } int main() { int n , m , k , i , j , x; scanf("%d%d%d" , &n , &m , &k); s = 0 , t = n + m + 1; for(i = 1 ; i <= m ; i ++ ) { scanf("%d" , &x); for(j = 1 ; j <= n ; j ++ ) add(i + n , t , 1 , x * (2 * j - 1)); } for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &f[i]); for(i = 1 ; i <= n ; i ++ ) { add(s , i , k , 0) , add(i , t , k - 1 , 0); scanf("%s" , str + 1); for(j = 1 ; j <= m ; j ++ ) if(str[j] == ‘1‘) add(i , j + n , 1 , -f[j]); } printf("%d\n" , mincost()); return 0; }
标签:font 地址 log return amp zoj ems cpp style
原文地址:http://www.cnblogs.com/GXZlegend/p/6809670.html