在公司年会上,做为互联网巨头51nod掌门人的夹克老爷当然不会放过任何发红包的机会。
第一行为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
Sample Output
现场有n排m列观众,夹克老爷会为每一名观众送出普通现金红包,每个红包内金额随机。
接下来,
夹克老爷又送出
最多k组
高级红包,每组高级红包会同时给一排或一列的人派发 ,每个高级红包的金额皆为x。
派发高级红包时,普通红包将会强制收回。同时,每个人只能得到一个高级红包。(好小气!)
现在求一种派发高级红包的策略,使得现场观众获得的红包总金额最大。
Input
1 <= n <= 10, 1 <= m <= 200
1 <= x <= 10^9,0 <= k <= n + m
接下来为一个n * m的矩阵,代表每个观众获得的普通红包的金额。普通红包的金额取值范围为1 <= y <= 10^9
3 4 1 5 10 5 7 2 10 5 10 8 3 9 5 4
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; }