51nod 1625 夹克爷发红包

来源:互联网 发布:c .net php开发招聘 编辑:程序博客网 时间:2024/04/29 18:07
在公司年会上,做为互联网巨头51nod掌门人的夹克老爷当然不会放过任何发红包的机会。

现场有n排m列观众,夹克老爷会为每一名观众送出普通现金红包,每个红包内金额随机。

接下来,夹克老爷又送出最多k组高级红包,每高级红包会同时给一排或一列的人派发 ,每高级红包的金额皆为x。

派发高级红包时,普通红包将会强制收回。同时,每个人只能得到一个高级红包。(好小气!)

现在求一种派发高级红包的策略,使得现场观众获得的红包总金额最大。
Input
第一行为n, m, x, k四个整数。1 <= n <= 10, 1 <= m <= 2001 <= x <= 10^9,0 <= k <= n + m接下来为一个n * m的矩阵,代表每个观众获得的普通红包的金额。普通红包的金额取值范围为1 <= y <= 10^9
Output
输出一个整数,代表现场观众能获得的最大红包总金额
Input示例
3 4 1 510 5 7 210 5 10 83 9 5 4
Output示例

78



其实具体还不是特别明白,先写下来.

首先利用二进制的特性来枚举行,对于行来说在把列的情况暴力求出来,之后选择最优情况

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>


#define  LL long long


using namespace std;


const int MAXN = 11;
const int MAXM = 201;


LL money[MAXN][MAXM];
int rlist[MAXN];


vector<LL> rowSum;
vector<LL> colSum;
vector<LL> h;       //  替换完行后,存储替换列的收益


//  检索第i种组合使用了几组高级红包
int count(int num)
{
    int ret = 0;
    while (num)
    {
        if (num & 1)
        {
            ret++;
        }
        num >>= 1;
    }
    return ret;
}


bool cmp(const LL &a, const LL &b)
{
    return a > b;
}


int main()
{
    LL n, m, x, k;
    LL sum = 0; //  所有红包总和
    scanf("%lld %lld %lld %lld", &n, &m, &x, &k);
    rowSum.resize(n, 0);
    colSum.resize(m, 0);
    h.resize(m, 0);


    for (int i = 0; i < n; ++ i)
    {
        for (int j = 0; j < m; ++ j)
        {
            scanf("%lld", &money[i][j]);
            rowSum[i] += money[i][j];
            colSum[j] += money[i][j];
            sum += money[i][j];
        }
    }
    LL row_max = 1ll * x * m;           //  整行为高级红包
    LL col_max = 1ll * x * n;           //  整列为高级红包
    LL ans = sum;                       //  初始为sum
    for (int i = 0; i < (1 << n); i++)  //  枚举所有行的组合 (1 << n)种组合
    {
        LL ret = sum;
        int idx = 0;
        int cnt = count(i);             //  高级红包使用组数
        if (k < cnt)                    //  如果超过了k,肯定不行,因为最多发k组
        {
            continue;
        }
        for (int j = 0; j < n; ++j)
        {
            if (i & (1 << j))           //  为1的标示这一行替换固定红包x,求出收益,并标示替换了哪些行
            {
                ret += row_max - rowSum[j];
                rlist[idx++] = j;       //  记录高级红包发放行数
            }
        }
        for (int j = 0; j < m; ++j)     //  求出替换完行后,每一列如果被替换所得的收益
        {
            h[j] = col_max - colSum[j];
            for (int l = 0; l < idx; ++l)
            {
                h[j] += money[rlist[l]][j] - x;
            }
        }
        sort(h.begin(), h.end(), cmp);  //  根据收益大小排序,得到最大收益的列的顺序
        for (int j = 0; j + cnt < k && h[j] > 0; ++j)   //  k-cnt剩余的就通过替换列来补充,直接加上列的收益即可
        {
            ret += h[j];
        }
        ans = max(ans, ret);            //  更新最大收益
    }
    printf("%lld\n", ans);


    return 0;
}

原创粉丝点击