http://acm.hdu.edu.cn/showproblem.php?pid=4415
把原数据分两组 一组 B 为 0 另一组 B 不为 0
B 不为 0 的那组 要么不死 要么全死
对于 B 全死的情况 这一组里面的人 主角可以用自己的剑杀死 也可以用敌人的剑杀死
如果用自己的剑杀死 如果杀死一个人 一定是 A 最小的那一个 如果是两个人 一定是 A 最小的那两个
枚举 比较最优解
代码及其注释:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#define LL long long
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int N=100005;
struct node
{
int a,b;
}mem[N];
int sum[N];//分组后 每组从最左边开始到第 i 个人 A的和
int num,cost;//最优 杀人数 和 花费
int n,m,L;//L 为分组 界限
bool cmpa(node x,node y)
{
return x.a<y.a;
}
bool cmpb(node x,node y)
{
return x.b<y.b;
}
int Search_B(int l,int r,int k)//二分查找第一组前几项和中最后一个小于等于 k 的位置
{
while(l<=r)
{
int mid=(l+r)>>1;
if(sum[mid]<=k)
l=mid+1;
else
r=mid-1;
}
return r;
}
void Test(int num1,int cost1,int k)//对各种情况进行计算取舍
{
int temp=min(k,L);
num1+=temp;
int w=Search_B(0,L-temp-1,m-cost1);
if(w>=0)
{
cost1+=sum[w];
num1+=(w+1);
}
if(num1>num||(num1==num&&cost1<cost))
{num=num1;cost=cost1;}
}
int main()
{
//freopen("data.txt","r",stdin);
int T;
scanf("%d",&T);
for(int ca=1;ca<=T;++ca)
{
scanf("%d %d",&n,&m);
for(int i=0;i<n;++i)
scanf("%d %d",&mem[i].a,&mem[i].b);
sort(mem,mem+n,cmpb);//先按B排序
for(L=0;L<n;++L)
{
if(mem[L].b)//找到 第一个 B 不为 0 的位置
break;
}
sort(mem,mem+L,cmpa);//将两组分别按 A 排序
sort(mem+L,mem+n,cmpa);
for(int i=0;i<L;++i)
{
sum[i]=mem[i].a;
if(i>0)
sum[i]+=sum[i-1];
}
int temp=0;
for(int i=L;i<n;++i)
{
sum[i]=mem[i].a;
if(i>L)
sum[i]+=sum[i-1];
temp+=mem[i].b;//累计第二组 B 的和
}
num=0;cost=0;
Test(0,0,0);//是第二组 都不死的情况
for(int i=1;i<=n-L;++i)
{
if(sum[L+i-1]<=m)//枚举 第二组有几个是主角直接杀死的
Test(n-L,sum[L+i-1],temp-(n-L-i));
}
printf("Case %d: %d %d\n",ca,num,cost);
}
return 0;
}
浙公网安备 33010602011771号