Wannafly挑戰賽26: B. 冥土追魂(思維題)
阿新 • • 發佈:2018-12-15
題目描述
有一天 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 */