P1392 取数 堆 贪心

取数

完成原题后食用有利于肠道健康

题目描述

在一个 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 2n+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 1n8

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 800 1\le n\le 800 1n800 1 ≤ k ≤ m ≤ 800 1\le k\le m\le 800 1km800

以上来自洛谷 以上来自洛谷 以上来自洛谷

解题思路

  • 先缩小思考范围,仅考虑 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
// .

/** @file stdc++.h
 *  This is an implementation file for a precompiled header.
 */

// 17.4.1.2 Headers

// C
#ifndef _GLIBCXX_NO_ASSERT
	#include 
#endif
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#if __cplusplus >= 201103L
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
#endif

// C++
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#if __cplusplus >= 201103L
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
#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;
}

最后抱怨一句,什么**出题人出的题啊!!!

你可能感兴趣的:(题解,算法,c++,贪心算法,c语言)