题目:
在公司年会上,做为互联网巨头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;
}