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; }