夹克老爷发红包(51nod1625)

在公司年会上,做为互联网巨头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
输出一个整数,代表现场观众能获得的最大红包总金额
Sample Input
3 4 1 5
10 5 7 2
10 5 10 8
3 9 5 4
Sample Output
78
 哎,这题很弄人的,特别是我这种小牛,在苦海中挣扎了很长时间。
  解: 又是一个特殊数据题,因为数据最多有10*200个,给我们很多数据结构去实现,很多人长时间没有头绪,是因为我们想问题总是先举例去想,比如我们可能会先想我把这一行发了,再把某某列发了,可是这样会影响以后的选择,于是我们很苦恼,两个限制条件,行与列,我们对行的操作必然会影响对列的操作,作为一个二维空间,某个数一定会同时在某一行与某一列上,如果光这样想,很难有思路,我们人脑不大可能去想出最终的思路,但是计算机可以,如果你有点做题经验,你可能会想到这个题是与贪心有关的,我们给计算机的是方法,具体的实现就交给计算机吧。步入正题,对于此题,我所见所闻的方法中较为简单的是这样的,因为行最多有10,对于每一行,我们要么发,要么不发,10行,那么有2^10种(每一行要末发要末不发),好了我们把任取一种情况,那么行的状态确定,比如说我们一共有5个红包,设我们选的情况是1110000010,(0代表没换,1代表换),那么此时还剩下5-4=1个红包,我们会将这一个红包在列上做贪心,如果任选一个列,我们都不能找出可发的(原来的列总钱数大于高级红包*所有行),那么我们就不换呗,如果能找到,就换,因为夹克老爷最多发出K组(注意是最多,而不是一定),没必要全发,我们只选择最好的情况去发。对于行的枚举我们用dfs,说实话,dfs这种思路我以前不知道,我以前也写过不少递归,猛然发现,我以前的递归思路命上名就叫dfs,其实数据结构套路都一样,重要的是思考能力。不多说上AC代码,这次代码纯自己想,自己编,没有过多优化,但是能够使人好理解,可能冗长,新手见谅。
 
 
 
 
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
long long c[1400];
long long stage[210];
int d;
int N;
int p;
long long sum_sum;
long long sum;
void dfs(long long a[][202],int k,int line,int m,long long x)
{
	int i,j;
	long long a_type[N][m];
	memset (a_type,0,sizeof(a_type));
	for (i=0;i<N;i++)
	for (j=0;j<m;j++)
	a_type[i][j]=a[i][j];
	if (k==0||line==N) //红包发完或者dfs到底我们就停止深搜,转而对列的贪心。 
	{
		for (j=0;j<m;j++)
		{
		 for (i=0;i<N;i++)
		  sum+=a[i][j];
		  stage[p]=sum;
		  p++;
		  sum=0;
	    }
	    sort(stage,stage+p);//对每一列的和进行排序,让N*高级红包优先与最少的比较 
	    for (i=0;i<p;i++)
	    {
		  if (k!=0&&stage[i]<N*x)
		  {
		  stage[i]=N*x;
		  k--;
	      }
		  sum_sum+=stage[i];
		} 
		  sum=0;
		  c[d]=sum_sum;
		  sum_sum=0;
		  d++;	
		  memset(stage,0,sizeof(stage));
		  p=0;
		return;
	}
	dfs (a,k,line+1,m,x);  //我们不给这行发红包 flag1 
	for (j=0;j<m;j++)   //我们给这行发红包 
	a[line][j]=x;
	dfs (a,k-1,line+1,m,x);  // flag2
	for (i=0;i<N;i++)  //flag2后,回溯一下,我们要保证a数组里的数的正确性,因为a数组是地址传递。flag1后不用,因为flag1没换 
	for (j=0;j<m;j++)
	a[i][j]=a_type[i][j];
}
int main ()
{
	int n,m,k,i,j,line;
	long long x,a[12][202],max;
	while (scanf("%d%d%lld%d",&n,&m,&x,&k)!=EOF)
	{
		memset(a,0,sizeof(a));
		memset(c,0,sizeof(c));
		memset(stage,0,sizeof(stage));
		d=0;
		N=n;
		p=0;
		max=sum_sum=sum=0;
		for (i=0;i<n;i++)
		 for (j=0;j<m;j++)
		 scanf("%lld",&a[i][j]);
		 line=0;
		 dfs (a,k,line,m,x);
		 for (i=0;i<d;i++)
		 if (c[i]>max)
		 max=c[i];
		 printf("%lld\n",max);
	}
	return 0;
 } 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值