题目链接:
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 */
时间: 2024-11-05 03:29:44