题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4415
题目意思:
要杀死n个敌人,每个敌人有两个属性a和b,a表示杀他所需要的能力值,b表示杀掉他后可以免费再杀b个敌人。告诉初始能力值,求能杀的最多的敌人,及杀掉那么多敌人的最小花费。
解题思路:
分类+贪心
这道题比较难,也比较经典。好题。
首先把敌人按b值是否为零分成两类A和B。A类表示b值不为零,B类表示b值为零。A、B分别按a从小到大排序
首先明确:
1、如果要杀A类,至少需要A[0].a能力值,并且可以把A类全部杀掉,而且还有多的免费名额可以再杀B类。
2、如果能杀A[0].a,则无论是花费杀A类,还是用免费名额杀A类,总的得到的杀B类的免费名额是不变的。
考虑三种情况:
1、先尽可能的杀B类,然后剩余的能力看能不能杀掉A中a值最小的A[0],然后把A类全部杀掉,再用免费的名额杀B。
2、先杀A[0],然后把A类全部杀掉,然后多的免费名额再杀B类,然后用多的花费来杀B类。
3、先杀A[0],此时不是用免费名额杀A类,而是比较A类和B类哪个a值较小,然后可能用花费能力值来杀A类,多出的那个免费名额来杀B类。
5 4
1 1
2 2
3 0
4 0
5 0
答案:5 3 如果要杀A类,至少要花费1,并且能杀A类的话,A类肯定可以全部杀完。此时得到的免费名额都是2(除开A的花销),而此时选择用花费来杀掉A类的2 2,这样就避免了花费更多的能力值去杀a值比较大的B类。
代码解释的很详细。
//#include<CSpreadSheet.h>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#include<cmath>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
struct Inf
{
int a,b;
};
vector<Inf>A,B;
int n,m;
bool cmp(Inf aa,Inf bb)
{
return aa.a<bb.a;
}
void cal(int &num,int &cost)
{
int num1=0,cost1=0,num2=0,cost2=0,sum=0; //num1,cost1表示先选A类 num2,cost2表示先尽可能的选B类
int i=0,j=0,p=B.size()-1; //p表示B类的最后一个元素
//printf("p:%d\n",p);
while(i<B.size()&&m-cost2>=B[i].a) //先尽可能的选B类
{
cost2+=B[i].a;
num2++;
i++;
}
i=0;
if(i<A.size()&&m>=A[i].a) //一开始就选A类,多的再选B类
{
num1+=A.size();
cost1=A[i].a;
while(i<A.size())
sum+=A[i++].b;
sum-=A.size()-1;
}
i=0;
if(i<A.size()&&m-cost2>=A[i].a) //把B选完后,再选A类看能不能把A类全部搞完
{
num2+=A.size();
cost2+=A[i].a;
num2+=sum; //如果行,一定可以选sum个 //至此先选B类的情况已经全部算完了
if(num2>n)
num2=n;
}
while(p>=0&&sum)
p--,sum--,num1++; //先选A类,再选B类的情况,还有可能继续支付花费来选B类
if(p<0) //说明先选A类,再选B类的情况可以不用花费就把B类全搞定 注意如果要消灭A类,至少要花费A[0].a
{
if((num1>num2)||(num1==num2&&cost1<cost2))
num=num1,cost=cost1;
else
num=num2,cost=cost2;
return ;
}
//printf("num1:%d cost1:%d num2:%d cost2:%d sum:%d p:%d\n",num1,cost1,num2,cost2,sum,p);
//system("pause");
//再考虑A类中,是考虑用免费的,还是用花费的,然后多出来的那个免费去搞B类
for(i=1,j=0;j<=p&&i<A.size()&&m-cost1>=min(A[i].a,B[j].a);)
{
if(A[i].a<=B[j].a) //选择花费来消灭当前这个A类,同时腾出来一个消灭B类
{ //注意总的量不变
cost1+=A[i].a;
p--;
i++;
num1++;
}
else //用花费来消灭B类
{
cost1+=B[j].a;
j++;
num1++;
}
}
//printf("::cost1:%d p:%d\n",cost1,p);
while(j<=p)
{
if(m-cost1>=B[j].a)
{
num1++;
cost1+=B[j].a;
j++;
}
else
break;
}
//printf("num1:%d cost1:%d num2:%d cost2:%d sum:%d p:%d\n",num1,cost1,num2,cost2,sum,p);
//system("pause");
if((num1>num2)||(num1==num2&&cost1<cost2))
num=num1,cost=cost1;
else
num=num2,cost=cost2;
return ;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t,kcas=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
A.clear();
B.clear();
for(int i=1;i<=n;i++)
{
Inf temp;
scanf("%d%d",&temp.a,&temp.b);
if(temp.b)
A.push_back(temp);
else
B.push_back(temp);
}
sort(A.begin(),A.end(),cmp);
sort(B.begin(),B.end(),cmp);
int num,cost;
cal(num,cost);
printf("Case %d: %d %d\n",++kcas,num,cost);
}
return 0;
}
/*
5 4
1 1
2 2
3 0
5 0
6 0
keys:5 3
*/