地址:https://www.nowcoder.com/acm/contest/212/B
思路:对于行列选取,是选择所有行中的最大值的最小值,当是并不能用贪心来做,例如
2 2 2
7 8
1 9
如果用贪心的话就是7+8=15,但是最小值是1+9=10
对于a[i][j]行按照由大到小排序,列按照行总和由小到大排序,选取顺序应该是以每次选择一整行来考虑的,即若k>=m,则选取前m个数一定是选取一整行的数的,可以这样考虑借鉴博客Wannafly挑战赛26: B. 冥土追魂(思维题)
假设第1行选择了前x个数字, 第2行选择了前y个数字,且x, y<m(都没选满一行)
首先肯定a[2][y+1]>a[1][x] (如果a[2][y+1]<=a[1][x]的话,那么选择a[2][y+1]更优,而不会选a[1][x]了) 那么可以得出a[2][y+1]>a[1][x] → a[2][y]>a[1][x] → a[2][y]>a[1][x+1],这样的话如果第二行选择前y-1个数字,第一行选择前x+1个一定更优
t1=k/m,t2=k%m, 那么可以对于t2可以枚举所有行,在取前t1(不包含t2选取行)行,保存最小值即可
Code :
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAX_N=1005;
const int MAX_M=1005;
int n,m,s;
int a[MAX_N][MAX_M];
struct node{
LL Sum[MAX_M];
int id;
bool operator<(const node &p)const{
return Sum[m]<p.Sum[m];
}
}d[MAX_N];
bool cmp(const int &a,const int &b){
return a>b;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>m>>s){
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
cin>>a[i][j];
for(int i=1;i<=n;++i)
sort(a[i]+1,a[i]+m+1,cmp);
for(int i=1;i<=n;d[i].id=i++)
for(int j=1;j<=m;++j)
d[i].Sum[j]=d[i].Sum[j-1]+a[i][j];
sort(d+1,d+n+1);
LL ans=1e18,num;
for(int i=1;i<=n;++i)
{
num=d[i].Sum[s%m];
for(int j=1,t=s/m;j<=n&&t>0;++j)
{
if(i==j&&s%m) continue;
num+=d[j].Sum[m];
--t;
}
ans=min(ans,num);
}
cout<<ans<<endl;
}
return 0;
}