数据结构题
【题意】
给定C个牛的CSAT分数score[i],和需要的资费aid[i],求上述C头牛的一个N元子集,使得其中位数最大,而资费总和<=f(特定的值)
【题解】
先按照score[i]排序,再枚举i,现在的任务就是求:
i前面哪n/2头牛的资费和最小;
i后面哪n/2头牛的资费和最小;
建大根堆维护即可。
【教训】
我一开始想在线算法,即一次将上述两问求出,后来发现这样做就繁了,可以两问分开做。
【代码】
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int,int> Pair;
const int maxn=100005;
Pair a[maxn];
int s[maxn],h[maxn],d1[maxn],d2[maxn];
int i,n,m,f,r,ans,sum;
bool cmp(Pair a,Pair b)
{
return a.first<b.first;
}
void Swap(int i,int j)
{
int t;
t=h[i];h[i]=h[j];h[j]=t;
}
void up(int i)
{
int j;
while (i>1)
{
j=i/2;
if (h[i]>h[j])
Swap(i,j);
else
break;
i=j;
}
}
void down(int i)
{
int j;
while (i*2<=r)
{
j=i*2;
if (j+1<=r && h[j+1]>h[j])
j++;
if (h[j]>h[i])
Swap(i,j);
else
break;
i=j;
}
}
void del(int x)
{
sum=sum-h[1]+x;
h[1]=x;
down(1);
}
void ins(int x)
{
h[++r]=x;
sum+=x;
up(r);
}
int main()
{
freopen("pin.txt","r",stdin);
freopen("pou.txt","w",stdout);
scanf("%d%d%d",&m,&n,&f);
for (i=1;i<=n;i++)
scanf("%d%d",&a[i].first,&a[i].second);
sort(a+1,a+n+1,cmp);
m=m/2;
r=sum=0;
for (i=1;i<=m;i++)
ins(a[i].second);
d1[m]=sum;
for (i=m+1;i<=n-m;i++)
{
if (a[i].second<h[1])
del(a[i].second);
d1[i]=sum;
}
r=sum=0;
memset(h,0,sizeof(h));
for (i=n;i>n-m;i--)
ins(a[i].second);
d2[n-m+1]=sum;
for (i=n-m;i>=m;i--)
{
if (a[i].second<h[1])
del(a[i].second);
d2[i]=sum;
}
ans=-1;
for (i=n-m;i>m;i--)
if (d1[i-1]+d2[i+1]+a[i].second<=f)
{
ans=a[i].first;
break;
}
cout << ans << endl;
return 0;
}