取数
完成原题后食用有利于肠道健康
题目描述
在一个 n n n 行 m m m 列的数阵中,你须在每一行取一个数(共 n n n 个数),并将它们相加得到一个和。对于给定的数阵,请你输出和前 k k k 小的取数方法。
输入格式
第一行,三个数 n , m , k n,m,k n,m,k。
第 2 ∼ n + 1 2\sim n+1 2∼n+1 行,每行 m m m 个正整数。
输出格式
一行共 k k k 个数,代表在每一行取一个数前 k k k 小的加和。
样例 #1
样例输入 #1
3 3 2
1 2 3
6 3 5
4 1 2
样例输出 #1
5 6
提示
对于 20 % 20\% 20% 的数据, 1 ≤ n ≤ 8 1\le n\le 8 1≤n≤8。
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 800 1\le n\le 800 1≤n≤800, 1 ≤ k ≤ m ≤ 800 1\le k\le m\le 800 1≤k≤m≤800。
以上来自洛谷 以上来自洛谷 以上来自洛谷
解题思路
- 先缩小思考范围,仅考虑 n = 2 n=2 n=2的情况。
- 每行可自行增序排序
- 枚举第 1 , 2 , 3 , … , k 1,2,3,…,k 1,2,3,…,k小的数,第1小由 a 1 , 1 + a 2 , 1 a_{1,1}+a_{2,1} a1,1+a2,1开始
- 若第 k k k小为 a 1 , i + a 2 , j a_{1,i}+a_{2,j} a1,i+a2,j,则 a 1 , i + 1 + a 2 , j a_{1,i+1}+a_{2,j} a1,i+1+a2,j、 a 1 , i + a 2 , j + 1 a_{1,i}+a_{2,j+1} a1,i+a2,j+1要纳入第 k + 1 k+1 k+1小的考虑行列
- 显然待考虑项可用堆维护
priority_queue<int> p_que;//大根堆
考虑由
i
i
i行扩展至
i
+
1
i+1
i+1行,
先求出前i行的最小
k
k
k项和,维护第
i
+
1
i+1
i+1行与其组合的前
k
k
k值即可
inline void find(int t, int s) {
for (int i = 1; i <= m; i++) {
s += a[t][i];
if (s + work1(t) > p_que.top()) {//如果选择此数并且加上可能取得的最小值还是不够小,说明不可行,没必要继续
return;
}
if (t == n && s < p_que.top()) {
p_que.pop();
p_que.push(s);
} else {
find(t + 1, s);
}
s -= a[t][i];
}
}
AC Code
// C++ includes used for precompiling -*- C++ -*-
// Copyright (C) 2003-2013 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <Licenses - GNU Project - Free Software Foundation>.
/** @file stdc++.h
* This is an implementation file for a precompiled header.
*/
// 17.4.1.2 Headers
// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdalign>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#endif
// C++
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif
using namespace std;
#define int long long
const int Maxn = 800 + 5;
int n, m, k, a[Maxn][Maxn];
int sum[Maxn], top;//sum数组计录每一行最小值的前缀和
int ans[Maxn];
priority_queue<int> p_que;
inline int work1(int i) {//第i行下最小值的和
return sum[n] - sum[i];
}
inline void find(int t, int s) {
for (int i = 1; i <= m; i++) {
s += a[t][i];
if (s + work1(t) > p_que.top()) {//如果选择此数并且加上可能取得的最小值还是不够小,说明不可行,没必要继续
return;
}
if (t == n && s < p_que.top()) {
p_que.pop();
p_que.push(s);
} else {
find(t + 1, s);
}
s -= a[t][i];
}
}
inline void work() {
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
sort(a[i] + 1, a[i] + m + 1);
sum[i] = sum[i - 1] + a[i][1];//前缀和
}
for (int i = 1; i <= k; i++) {
p_que.push(INT_MAX);
}
find(1, 0);
while (p_que.size()) {//大根堆需反向输出
ans[++top] = p_que.top();
p_que.pop();
}
for (int i = k; i >= 1; i--) {
cout << ans[i] << ' ';
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
work();
return 0;
}
最后抱怨一句,什么**出题人出的题啊!!!