P1392 取数 堆 贪心

文章讲述了如何在给定的数阵中,每行取一个数,选取前k个和最小的数的解题策略,利用优先队列和大根堆数据结构进行优化计算。
摘要由CSDN通过智能技术生成

取数

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

题目描述

在一个 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
// <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;
}

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

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值