nod-1625-夹克爷发红包

题目:

在公司年会上,做为互联网巨头51nod掌门人的夹克老爷当然不会放过任何发红包的机会。

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

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

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

现在求一种派发高级红包的策略,使得现场观众获得的红包总金额最大。
Input
第一行为n, m, x, k四个整数。

1 <= n <= 10, 1 <= m <= 200
1 <= x <= 10^9,0 <= k <= n + m

接下来为一个n * m的矩阵,代表每个观众获得的普通红包的金额。普通红包的金额取值范围为1 <= y <= 10^9
Output
输出一个整数,代表现场观众能获得的最大红包总金额
Input示例
3 4 1 5
10 5 7 2
10 5 10 8
3 9 5 4
Output示例
78

思路:

                贪心!!!
最初想法,另个纬度贪心,所有行的和从小到大排序,所有列的和从小到大排序,然后用行最大值也就是x*m-行最小值 和列最大值x*n -列最小值,哪个大,表明要替换一样还是一列;替换后,更新每个人的红包以及所有影响的行和列的和,然后重新排序,重复以上过程;感觉思路很通畅,但是有一个case永远过不了;还没想清楚原因,估计是这种双贪心不一定能得到最优解吧????
后来看了下讨论,说是枚举一个维度,然后另一个维度贪心,按照这个方式实现了一下,没有问题!
也就是总共10行,枚举所有可能也就1024中可能,这些可能当中,全部替换行,如果替换完行后,还没用完k组红包,那么剩下的替换收益最大的列,也就是贪心;这样枚举所有行的组合,最终求出最大收益值。

代码一,有个case不过

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
#define  LL long long
using namespace std;
LL money[15][205];
typedef struct LOC
{
    int loc;
    LL  sum;
}Loc;
vector<Loc> row;
vector<Loc> col;
bool cmp(const Loc &a, const Loc &b)
{
    return a.sum < b.sum;
}
int main()
{
    LL n,m,x,k;
    LL ans = 0;
    scanf("%lld %lld %lld %lld", &n,&m,&x,&k);
    row.resize(n);
    col.resize(m);
    for (int i = 0; i < n; ++ i)
    {
        for (int j = 0; j < m; ++ j)
        {
            scanf("%d", &money[i][j]);
            row[i].sum+=money[i][j];
            row[i].loc = i;
            col[j].sum+=money[i][j];
            col[j].loc = j;
            ans += money[i][j];
        }
    }
    sort(row.begin(), row.end(),cmp);
    sort(col.begin(), col.end(), cmp);
    LL row_max = (long long)x*m;
    LL col_max = (long long)x*n;
    //k++;
    while(k != 0)
    {
        if (row[0].sum >= row_max && col[0].sum >= col_max)
        {
            break;
        }
        if (row_max - row[0].sum > col_max - col[0].sum)
        {
            ans += row_max - row[0].sum;
            int r = row[0].loc;
            row[0].sum = row_max;
            for (int j = 0; j < col.size(); ++ j)
            {
                int c = col[j].loc;
                col[j].sum = col[j].sum - money[r][c]+x;
                money[r][c] =x; 
            }
        }
        else
        {
            ans +=  col_max - col[0].sum;
            int c = col[0].loc;
            col[0].sum=col_max;
            for (int i = 0; i < row.size(); ++ i)
            {
                int r = row[i].loc;
                row[i].sum = row[i].sum - money[r][c]+x;
                money[r][c]=x;
            }
        }
        sort(row.begin(), row.end(),cmp);
        sort(col.begin(), col.end(), cmp);
        k --;
    }
    printf("%lld\n", ans);
    return 0;
}


代码二,all通过

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
#define  LL long long
using namespace std;
LL money[15][205];
int rlist[15];
vector<LL> row;
vector<LL> col;
vector<LL> h;

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);
    row.resize(n,0);
    col.resize(m,0);
    h.resize(m,0);
    for (int i = 0; i < n; ++ i)
    {
        for (int j = 0; j < m; ++ j)
        {
            scanf("%d", &money[i][j]);
            row[i]+=money[i][j];
            col[j]+=money[i][j];
            sum += money[i][j];
        }
    }
    LL row_max = (long long)x*m;
    LL col_max = (long long)x*n;
    LL ans = sum;
    for (int i = 0; i < (1<<n); ++ i)//枚举所有行的组合
    {
        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 - row[j];
                rlist[idx++] = j;
            }
        }
        for (int j = 0; j < m; ++ j)//求出替换完行后,每一列如果被替换所得的收益
        {
            h[j] = col_max - col[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 && j < m &&h[j]>0; ++ j)//k-cnt剩余的就通过替换列来补充,直接加上列的收益即可
        {
            ret += h[j];
        }
        ans = max(ans,ret);//更新最大收益
    }
    printf("%lld\n", ans);
    
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值