编程之美2014年资格赛第三题——格格取数(已过大数据)
2014-04-14 15:44
423 查看
题目3 :格格取数
时间限制:2000ms
单点时限:1000ms
内存限制:256MB
描述
给你一个m x n (1 <=m, n <= 100)的矩阵A (0<=aij<=10000),要求在矩阵中选择一些数,要求每一行,每一列都至少选到了一个数,使得选出的数的和尽量的小。
输入
多组测试数据。首先是数据组数T
对于每组测试数据,第1行是两个正整数m, n,分别表示矩阵的行数和列数。
接下来的m行,每行n个整数,之间用一个空格分隔,表示矩阵A的元素。
输出
每组数据输出一行,表示选出的数的和的最小值。
数据范围
小数据:1 <= m, n<= 5
大数据:1 <= m, n<= 100
样例输入
2
3 3
1 2 3
3 1 2
2 3 1
5 5
1 2 3 4 5
5 1 2 3 4
4 5 1 2 3
3 4 5 1 2
2 3 4 5 1
样例输出
Case 1: 3
Case 2: 5
题目要求使得每一行、每一列都至少有一个数被取出,并使得取出的和最小。开始就觉得是每行、每列都去一个这样是最小的。网上流传很广的一组样例:
4 4
1 1 100
100 100 1
100 100 1
能够很轻松的否定掉这个思路。开始按照这个思路写的最小费用最大流,是因为最大流为max(n,m)。但是,显然不是这个样子的。真正的流量是不能够确定的。所以,直接用最小费用最大流就不行了。
那么,这里我们可以进行一些处理。让没有必要的流量直接从源点流向汇点。这样一来,总流量就一定是n*m了。而对于要通过行点和列点的流量是多少呢?经过分析可以发现。如果第i行和第j列都已经取出了一个数(不是a[i][j]时),那么a[i][j]必然是不会被取出的。
现在要解决的问题就是如何保证行点和列点至少流过一次,且不会存在两个已经流过的点在被同时流一次。将行点和列点进行拆分。共2*m个行点和2*n个列点。那么就是源点到m个行点的流量为1,费用为负无穷,n个列点到汇点的流量为1,费用为负无穷。剩下的m个行点的流量为n,费用为0,n个列点的流量为m,费用为0.源点到汇点的流量为n*m,费用为0.同时,我们需要建立一个超级源点,保证总流量一定是n*m。即超级源点到源点的流量是n*m。这样就能保证得到的费用是最小值了,最后的结果就是得到的费用加上(n+m)*负无穷。
可以看出来,如果某个行点和某个列点已经被选择,那么如果存在一个流流过这两个点,那么代价必然是a[i][j],显然不如直接从源点流向汇点。所以,就避免出现第i行和第j列都已经取出了一个数且不是a[i][j]时,取出了a[i][j]这种多余情况。同时,可以得到,负无穷只要能使最大边权+负无穷小于0即可。
时间限制:2000ms
单点时限:1000ms
内存限制:256MB
描述
给你一个m x n (1 <=m, n <= 100)的矩阵A (0<=aij<=10000),要求在矩阵中选择一些数,要求每一行,每一列都至少选到了一个数,使得选出的数的和尽量的小。
输入
多组测试数据。首先是数据组数T
对于每组测试数据,第1行是两个正整数m, n,分别表示矩阵的行数和列数。
接下来的m行,每行n个整数,之间用一个空格分隔,表示矩阵A的元素。
输出
每组数据输出一行,表示选出的数的和的最小值。
数据范围
小数据:1 <= m, n<= 5
大数据:1 <= m, n<= 100
样例输入
2
3 3
1 2 3
3 1 2
2 3 1
5 5
1 2 3 4 5
5 1 2 3 4
4 5 1 2 3
3 4 5 1 2
2 3 4 5 1
样例输出
Case 1: 3
Case 2: 5
题目要求使得每一行、每一列都至少有一个数被取出,并使得取出的和最小。开始就觉得是每行、每列都去一个这样是最小的。网上流传很广的一组样例:
4 4
1 1 100
100 100 1
100 100 1
能够很轻松的否定掉这个思路。开始按照这个思路写的最小费用最大流,是因为最大流为max(n,m)。但是,显然不是这个样子的。真正的流量是不能够确定的。所以,直接用最小费用最大流就不行了。
那么,这里我们可以进行一些处理。让没有必要的流量直接从源点流向汇点。这样一来,总流量就一定是n*m了。而对于要通过行点和列点的流量是多少呢?经过分析可以发现。如果第i行和第j列都已经取出了一个数(不是a[i][j]时),那么a[i][j]必然是不会被取出的。
现在要解决的问题就是如何保证行点和列点至少流过一次,且不会存在两个已经流过的点在被同时流一次。将行点和列点进行拆分。共2*m个行点和2*n个列点。那么就是源点到m个行点的流量为1,费用为负无穷,n个列点到汇点的流量为1,费用为负无穷。剩下的m个行点的流量为n,费用为0,n个列点的流量为m,费用为0.源点到汇点的流量为n*m,费用为0.同时,我们需要建立一个超级源点,保证总流量一定是n*m。即超级源点到源点的流量是n*m。这样就能保证得到的费用是最小值了,最后的结果就是得到的费用加上(n+m)*负无穷。
可以看出来,如果某个行点和某个列点已经被选择,那么如果存在一个流流过这两个点,那么代价必然是a[i][j],显然不如直接从源点流向汇点。所以,就避免出现第i行和第j列都已经取出了一个数且不是a[i][j]时,取出了a[i][j]这种多余情况。同时,可以得到,负无穷只要能使最大边权+负无穷小于0即可。
#include <vector> #include <list> #include <map> #include <set> #include <deque> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <ctime> #include <cstring> #define INF 99999999 #define MAXN 20000 using namespace std; const int M = 100010;//边 const int N = 500; struct Node//边,点f到点t,流量为c,费用为w { int st, ed; int flow, cost; int next; }edge[M]; int head , dis , q , pre , cnt;//cnt为已添加的边数,head为邻接表,dis为花费,pre为父亲节点 bool vis[M]; int n, m, s ,t; int graph[110][110]; void init() { memset(head, -1, sizeof(head)); cnt = 0; } void add_edge(int f, int t, int d1, int d2, int w) {//f到t的一条边,流量为d1,反向流量d2,花费w,反向边花费-w(可以反悔) edge[cnt].st = f; edge[cnt].ed = t; edge[cnt].flow = d1; edge[cnt].cost = w; edge[cnt].next = head[f]; head[f] = cnt++; edge[cnt].st = t; edge[cnt].ed = f; edge[cnt].flow = d2; edge[cnt].cost = -w; edge[cnt].next = head[t]; head[t] = cnt++; } bool spfa(int s, int t, int n) { int i, tmp, l, r; memset(pre, -1, sizeof(pre)); for(i = 0; i < n; ++i) { dis[i] = INF; } dis[s] = 0; q[0] = s; l = 0, r = 1; vis[s] = true; while(l != r) { tmp = q[l]; l = (l + 1) % (n + 1); vis[tmp] = false; for(i = head[tmp]; i!=-1; i = edge[i].next) { if(edge[i].flow && dis[edge[i].ed] > dis[tmp] + edge[i].cost) { dis[edge[i].ed] = dis[tmp] + edge[i].cost; pre[edge[i].ed] = i; if(!vis[edge[i].ed]) { vis[edge[i].ed] = true; q[r] = edge[i].ed; r = (r + 1) % (n + 1); } } } } if(pre[t] == -1) { return false; } return true; } void MCMF(int s, int t, int n, int &flow, int &cost) {//起点s,终点t,点数n,最大流flow,最小花费cost int tmp, arg; flow = cost = 0; while(spfa(s, t, n)) { arg = INF; tmp = t; while(tmp != s) { arg = min(arg, edge[pre[tmp]].flow); tmp = edge[pre[tmp]].st; } tmp = t; while(tmp != s) { edge[pre[tmp]].flow -= arg; edge[pre[tmp] ^ 1].flow += arg; tmp = edge[pre[tmp]].st; } flow += arg; cost += arg * dis[t]; } } int main() { int x; scanf("%d",&x); for(int r = 1;r <= x;r++) { init(); scanf("%d%d",&m,&n); for(int i = 1;i <= m;i++) { for(int j = 1;j <= n;j++) { scanf("%d",&graph[i][j]); add_edge(i,2*m+j,1,0,graph[i][j]); add_edge(i,2*m+n+j,1,0,graph[i][j]); add_edge(i+m,2*m+n+j,1,0,graph[i][j]); add_edge(i+m,2*m+j,1,0,graph[i][j]); } } for(int i = 1;i <= m;i++) { add_edge(0,i,1,0,-MAXN); add_edge(0,i+m,n,0,0); } t = 2 * m + 2 * n + 1; for(int i = 1;i <= n;i++) { add_edge(i+2*m,t,1,0,-MAXN); add_edge(2*m+n+i,t,m,0,0); } s = t + 1; add_edge(s,0,n*m,0,0); add_edge(0,t,n*m,0,0); int flow ,cost; MCMF(s,t,t+2,flow,cost); printf("Case %d: %d\n",r,cost+(m+n)*MAXN); } return 0; }
相关文章推荐
- 编程之美2014年资格赛第三题格格取数
- 2014编程之美资格赛第三题-格格取数
- 编程之美2014资格赛 hihoCoder 题目3:格格取数
- 2014年百度之星资格赛第三题Xor Sum
- 2014年百度之星程序设计大赛 - 资格赛 第三题 Xor Sum
- 2014年百度之星程序设计大赛 - 资格赛 第三题 Xor Sum
- 宋体数据2013编程之美全国挑战赛资格赛之传话游戏
- 2014年百度之星资格赛第三题Xor Sum
- 编程之美资格赛第三题:树上的三角形
- 编程之美2014 资格赛题目3 : 格格取数
- 编程之美2014资格赛 大神与三个小伙伴 小数据
- 编程之美2014资格赛题目3:格格取数
- 【编程之美资格赛】题目3 : 格格取数
- nginx proxy_buffer_size 解决后端服务传输数据过多,其实是header过大的问题
- 数据条数过大时,分片采集
- 关于fielddata数据占用内存过大的解决方法
- Silverlight 中文教程第三部分:使用 Networking取回数据并填充DataGrid
- 【C 语言的学习】第三回合:数据类的大集合
- redis 数据统计(用自增id防止同一秒并发过大没统计成功)
- 数据结构与算法 第三课