题意:
思路:
数据只有15,直接枚举所有情况即可
通过二进制枚举行,每次枚举的意义是枚举几行以及枚举的哪几行
假设该次枚举cnt行,那么剩下的肯定都选的是列,只用找到枚举完行之后,剩余每列之和的前k-cnt大的加在总和里即可
__builtin_popcount(i)计算i的二进制中1的个数
一些__builtin_函数
为什么不能贪心,每次找行或列的最大值
因为选列行或列之和,对列或行有影响,即选不同的行产生的最大不一样
例如:
3 3 2
101 101 1
10 5 1
2 10 1
按照贪心的话应该选第一行,然后第二列 结果是203+15=218
但是先选第一行,然后第二行的结果是113+116=229
显然贪心有问题
代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
#include <math.h>
#include <map>
#include <queue>
#include <set>
#include <stack>
typedef long long ll;
#define PII make_pair
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int MAXN=1e5+50;
const int inf=0x3f3f3f3f;
const int M=5000*4;
int n,m,k;
int a[20][20];
ll h[20];//每一行的和
ll p[20];//每一列的和
int main()
{
scanf("%d%d%d",&n,&m,&k);
ll sum=0;
rep(i,1,n)
rep(j,1,m){
scanf("%d",&a[i][j]);
h[i]+=a[i][j];
sum+=a[i][j];
}
if(k>=n||k>=m){
printf("%lld\n",sum);
return 0;
}
k=min(k,min(n,m));
ll ans=0;
for(int i=0;i<(1<<n);i++){
int cnt=__builtin_popcount(i);//有cnt个1,要选cnt行
if(cnt>k)continue;
sum=0;
memset(p,0,sizeof(p));//记得清零
for(int j=0;j<n;j++)if(i&(1<<j))sum+=h[j+1];
for(int j=0;j<n;j++)
for(int kk=1;kk<=m;kk++)if(!(i&(1<<j)))p[kk]+=a[j+1][kk];//在选cnt行之后,每一列的和
sort(p+1,p+m+1);
for(int j=1;j<=k-cnt;j++)
sum+=p[m-j+1];
ans=max(ans,sum);
}
printf("%lld\n",ans);
return 0;
}