书籍简介
第一章 字符串
1.0 本章导读
1.1 旋转字符串
1.2 字符串包含
1.3 字符串转换成整数
1.4 回文判断
1.5 最长回文子串
1.6 字符串的全排列
1.10 本章习题
第二章 数组
2.0 本章导读
2.1 寻找最小的 k 个数
2.2 寻找和为定值的两个数
2.3 寻找和为定值的多个数
2.4 最大连续子数组和
2.5 跳台阶
2.6 奇偶排序
2.7 荷兰国旗
2.8 矩阵相乘
2.9 完美洗牌
2.10 K个最小和 (UVA 11997 K Smallest Sums)
2.15 本章习题
第三章 树
3.0 本章导读
3.1 红黑树
3.2 B树
3.3 最近公共祖先LCA
3.5 R树:处理空间存储问题
3.10 本章习题
第四章 查找匹配
4.1 有序数组的查找
4.2 行列递增矩阵的查找
4.3 出现次数超过一半的数字
第五章 动态规划
5.0 本章导读
5.1 最大连续乘积子串
5.2 字符串编辑距离
5.3 格子取数
5.4 交替字符串
5.6 最长递增子序列
5.10 本章习题
第六章 海量数据处理
6.0 本章导读
6.1 关联式容器
6.2 分而治之
6.3 simhash算法
6.4 外排序
6.5 MapReduce
6.6 多层划分
6.7 Bitmap
6.8 Bloom filter
6.9 Trie树
6.10 数据库
6.11 倒排索引
6.15 本章习题
第七章 机器学习
7.1 K 近邻算法
7.2 支持向量机
附录 更多题型
附录A 语言基础
附录B 概率统计
附录C 智力逻辑
附录D 系统设计
附录E 操作系统
附录F 网络协议
2.10 K个最小和 (UVA 11997 K Smallest Sums) - 《程序员编程艺术:面试和算法心得》 - 光年文档管理系统(Light Year Doc)
网站首页
2.10 K个最小和 (UVA 11997 K Smallest Sums)
### 题目大意: You're given k arrays, each array has k integers. There are kk ways to pick exactly one element in each array and calculate the sum of the integers. Your task is to find the k smallest sums among them. #### Input There will be several test cases. The first line of each case contains an integer k (2<=k<=750). Each of the following k lines contains k positive integers in each array. Each of these integers does not exceed 1,000,000. The input is terminated by end-of-file (EOF). The size of input file does not exceed 5MB. #### Output For each test case, print the k smallest sums, in ascending order. #### Sample Input 3 1 8 5 9 2 5 10 7 6 2 1 1 1 2 #### Output for the Sample Input 9 10 12 2 2 给定一个k*k的一个矩阵,如果让你在每一行取出一个数,再将每一行取出的数相加,那么总共可以得到k^k种相加方法,现在让你求出这k^k个结果中最小的k个结果。 ### 分析与解法 仔细分析这个题目我们会发现其实这个问题是满足最优子结构的,比如: 如果我们已经计算出了前m行,每行取出一个数相加的最小的k个结果,分别是DP[1],DP[2]...DP[k] (注意这里的DP表示的是前m行每行一个相加的最小的前k个值) 假设第m+1行的值是A[1],A[2]...A[k] (注意这里的A[i]表示的是第m+1行的第i个数) 当我们推倒到第m+1行时,由于我们只计算了前m行的前k个最小值,那我们是不是有必要多计算一些来推导出第m+1行的前k个最小值呢? 答案是不必要的,我们可以通过以下数学公式严格证明: 设DP[x]是前m行通过计算得出的第x(x>k)小的和,如果上述的假设成立,那么我们可以列出不等式: DP[x] + A[y] < DP[m] + A[n] (1) (DP[m]+A[n]表示只通过DP[1,2...k]计算出的前m+1行第k小的和) 上述不等式的含义是指在第m+1行存在一个数A[y],使得DP[x]+A[y]是前m+1行中前k小的结果。 同时,我们注意到: x>k ==> DP[x] > DP[k] (2) A[y] >= A[1] (3) 由上面三个不等式(1),(2),(3)我们可以得到: DP[k]+A[1] <= DP[x]+A[y] < DP[m]+A[n] DP[k]+A[1] < DP[m]+A[n] 之前我们说过DP[m] + A[n] 是前m行第k大的和,然而:比DP[k]+A[1]小的数已经有 (DP[1]+A[1]),(DP[2]+A[1])...(DP[k-1]+A[1])共计k-1个, 所以DP[k]+A[1]是第k个最小的和,与假设的DP[m]+A[n]是第k个最小的和相矛盾,所以假设不成立。得证。 通过以上的证明我们可以得出结论要计算第m+1行的前k个最小和是只需要计算出前m行的前k个最小的和即可。这时,我们的目标就转化为了计算一个2*k的数组,在第一行取一个数,在第二行取一个数,得到k^2个和,求他们当中的最小的k个和。 为了计算它,我们把这n^2个数组织成如下n个有序表: 表1: A1+B1 <= A1+B2<=A1+B3<=...... 表2: A2+B1 <= A2+B2<=A2+B3<=...... . 表n: An+B1 <= An+B2<=An+B3<=...... 这时我们用一个二元组(sum, b)来保存以上的每一个元素,其中sum=A[a] + B[b]. 为什么不保存A的下标a呢?因为我们用不到a的值。如果我们需要在表(sum, b)中赵到下一个元素(sum', b+1),只要计算sum' = s - B[b] + B[b+1],不需要知道a是多少。 ### 实现代码 ```c #include <cstdio> #include <algorithm> #include <queue> using namespace std; int a[800][800],k; struct node { int s,b; bool operator<(const node &a) const { return s>a.s; } }; void merge(int *A,int *B,int *C,int n) { node tmp; priority_queue<node> q; for(int i=0;i<n;i++) { tmp.s=A[i]+B[0],tmp.b=0; q.push(tmp); } for(int i=0;i<n;i++) { tmp=q.top(); q.pop(); C[i]=tmp.s; if(tmp.b+1<n) { tmp.s=tmp.s-B[tmp.b]+B[tmp.b+1]; tmp.b++; q.push(tmp); } } } int main() { while(~scanf("%d",&k)) { for(int i=0;i<k;i++) { for(int j=0;j<k;j++) scanf("%d",&a[i][j]); sort(a[i],a[i]+k); } for(int i=1;i<k;i++) merge(a[0],a[i],a[0],k); for(int i=0;i<k;i++) printf("%d%s",a[0][i],i!=k-1?" ":"\n"); } return 0; } ``` 参考 http://www.cnblogs.com/gj-Acit/p/3583480.html
上一篇:
2.9 完美洗牌
下一篇:
2.15 本章习题