大致意思就是说,总共有m个商店,有n个朋友。每个商店里卖n中货物,每个商店的第i种货物能给第i个朋友带来不同的高兴值,且只能从n-1个商店里面选出n件礼物,使得n个朋友的高兴值尽量大一点,然后输出在这尽量大的高兴值队列中最小的高兴值是多少。Vlad wants to maximize the minimum of the joys of his friends.
首先,要明确,可能满足n个朋友的礼物的队列是不唯一的,也就是说,会有很多不同的值能够满足,要求得最优解,就必须要一次次逼近,所以,选用二分+贪心的算法。
选择二分理由:以所有给出的欢乐之为队列,求出最小值最大值,然后设置为l和r进行二分,当求得mid的时候,判断每个商店大于等于该值的欢乐值有多少个,当满足条件时即可更新答案。
选择贪心策略:由于对于每一个mid值的每个商店可能有不同的不同的i个数满足要求,所以按照个数从大到小能更好的找到满足n的个数。
需要注意的两点:1.极限测试样例是1e4 2.m*n≤2e5
初学二分注意:
1.必须找到一个单调的序列,这是能够二分最基本的要求。
2.必须找到往左走还是往右走的判定条件。
ac码:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int si;
int cnt;
}ar[200100];
bool cmp(node a,node b)
{
return a.cnt>b.cnt;
}
vector<int> vec[200100];
int book[200100],m,n;
bool judge(int val)
{
int i,j,cnt,flag,lim;
cnt=0;
lim=0;
for(i=0;i<m;i++)
{
ar[i].si=i;
ar[i].cnt=0;
for(j=0;j<n;j++)
{
book[j]=0;
if(vec[i][j]>=val)
{
ar[i].cnt++;
}
}
}
sort(ar,ar+m,cmp);
for(i=0;i<m;i++)
{
flag=0;
for(j=0;j<n;j++)
{
if(vec[ar[i].si][j]>=val)
{
if(book[j]==0)
{
cnt++;
book[j]=1;
flag=1;
if(cnt==n) break;
}
}
}
if(flag==1)
{
lim++;
if(lim>n-1 && cnt<n) return false;
else if(cnt==n) break;
}
}
if(cnt==n&& lim<=n-1) return true;
else return false;
}
int main()
{
int k;
scanf("%d",&k);
while(k--){
int i,j,date;
int minn=-1,maxx=0x3f3f3f3f,le,ri,mid,ans=-1;
scanf("%d%d",&m,&n);
for(i=0;i<m;i++)
{
vec[i].clear();
}
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&date);
vec[i].push_back(date);
minn=min(minn,date);
maxx=max(maxx,date);
}
}
le=minn;
ri=maxx;
while(le<=ri)
{
mid=(le+ri)/2;
if(judge(mid))
{
ans=mid;
le=mid+1;
}
else
{
ri=mid-1;
}
}
printf("%d\n",ans);
}
return 0;
}