1. 程式人生 > >Wannafly挑戰賽26: B. 冥土追魂(思維題)

Wannafly挑戰賽26: B. 冥土追魂(思維題)

題目描述

有一天 Misaka 和 Kuroko 在玩一個關於冥土追魂的遊戲.... Misaka和Kuroko在一個 n x m 的棋盤上玩遊戲,每個格子上都放著一些呱太。遊戲共進行 k 回合,每一回合 Kuroko會選**有呱太**的一行 i,在這之後Misaka會選擇一列 j ,並拿走格子 (i, j) 上的所有呱太,Misaka希望自己拿走的呱太儘可能多,而Kuroko不想讓Misaka拿走很多呱太,所以她希望拿走的呱太儘可能少。 在一旁圍觀的恆溫死神希望預測結果,請你預測在雙方都採取最優策略的情況下,Misaka最終能拿走呱太的數量。

輸入描述:

第一行三個數 n, m, k。 
接下來 n 行,每行 m 個數,第 i 行第 j 個數表示棋盤第 i 行第 j 列上的呱太數量 ai,j。

輸出描述:

輸出共一個數,表示在你的預測下,Misaka最終能拿走呱太的數量。

輸入

3 2 4
5 7
3 2
8 5

輸出

17

將矩陣的每一行從大到小排序

這樣問題就變成了:每一行都選擇一段字首, 可以不選,求出總共選了剛好k個數的最小值

可以證明:最優情況下一定是最多隻能有一行只選一部分,剩下n-1行要不整行全選,要不不選

也就是說對於當前k,暴力列舉哪一行選擇前(k%m)個,然後剩下n-1行中選擇所有和最小的(k/m)行就可以了

複雜度O(nlogm+n²)

證明如下:

假設第1行選擇了前x個數字, 第2行選擇了前y個數字,且x, y<m(都沒選滿一行)

那麼可以得出a[2][y+1]>a[1][x] → a[2][y]>a[1][x] → a[2][y]>a[1][x+1],這樣的話如果第二行選擇前y-1個數字,第一行選擇前x+1個一定更優, 證畢

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
LL jz[1005][1005];
int a[1005][1005];
typedef struct Res
{
	int id;
	LL sum;
	bool operator < (const Res &b) const
	{
		if(sum<b.sum)
			return 1;
		return 0;
	}
}Res;
Res s[200005];
int comp(int x, int y)  {  return x>y;  };
int main(void)
{
	LL ans, now;
	int n, m, k, i, j, cnt;
	scanf("%d%d%d", &n, &m, &k);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
			scanf("%d", &a[i][j]);
		sort(a[i]+1, a[i]+m+1, comp);
	}
	ans = 0;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
			jz[i][j] = jz[i][j-1]+a[i][j];
		ans += jz[i][m];
		s[i].sum = jz[i][m], s[i].id = i;
	}
	sort(s+1, s+n+1);
	for(i=1;i<=n;i++)
	{
		cnt = 0;
		now = jz[i][k%m];
		for(j=1;j<=n;j++)
		{
			if(cnt==k/m)
				break;
			if(s[j].id==i && k%m)
				continue;
			now += s[j].sum;
			++cnt;
		}
		ans = min(ans, now);
	}
	printf("%lld\n", ans);
	return 0;
}
/*
2 3 1
7 6 5
10 1 1
*/