牛客练习赛58C 矩阵消除游戏

题目链接

【题意】:n*m的二维数组, 有k次选择机会,每次可以取走那一行或者那一列。取完后得到那一行或者那一列的和的价值同时将那一行或那一列的值全部变为0。问最大价值。(\(n,m ≤ 15\))

【思路】:首先如果只能取行或者只能取列的话我们可以预处理出每行或每列的和然后贪心选就可以了。但是这里每一个操作既能取行又能去列,所以不能直接贪心,因为当前操作会对后面的操作有影响,有后效性。再考虑这个数据范围, 我们可以想到用01串枚举所选的行,0表示没选, 1表示选了。枚举出所选的行之后,我们再对剩下的列做贪心处理, 就能解决这个问题了。时间复杂度\(O(2^n*(nlogn + mn))\)

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 20;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6, pi = acos(-1);
int gcd(int a,int b)    { return b == 0 ? a : gcd(b, a % b); }

int n, m, k;
int arr[maxn][maxn];
int vis[maxn], sumr[maxn], sumc[maxn];

int deal(int x)
{
    memset(vis, 0, sizeof(vis));
    int tot = 0, ans = 0;
    while(x)
    {
        if(x & 1)   ans++;
        vis[++tot] = x & 1;
        x >>= 1;
    }
    return ans;
}
bool cmp(int a, int b) { return a > b; }
LL res;

int main()
{
    scanf("%d %d %d", &n, &m, &k);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            scanf("%d", &arr[i][j]);
            sumr[i] += arr[i][j];
        }
    }
    if(k >= min(m, n))  k = min(m, n);
    for(int i = 0; i <= (1 << n) - 1; ++i){         //枚举行
        int tot = deal(i);
        int totc = k - tot;
        if(totc < 0 || totc > m)    continue;
        LL ans = 0;
        for(int i = 1; i <= n; ++i){
            if(vis[i])  ans += 1LL * sumr[i];
        }
        memset(sumc, 0, sizeof(sumc));
        for(int i = 1; i <= n; ++i){
            if(vis[i])  continue;
            for(int j = 1; j <= m; ++j){
                sumc[j] += arr[i][j];
            }
        }
        sort(sumc + 1, sumc + m + 1, cmp);
        for(int i = 1; i <= totc; ++i){
            ans += 1LL * sumc[i];
        }
        res = max(ans, res);
    }
    printf("%lld\n", res);
    system("pause");
}
posted @ 2020-02-29 20:53  StungYep  阅读(123)  评论(0编辑  收藏  举报