描述:
有n个命令,要通过p个,某主席要在通过的p个中选择k个接受。每个任务有两个值ai,bi,ai表示如果改主席接受该命令,他的头发变灰的数量,bi表示如果主席不接受该命令时,议员不高兴值。
对于通过的p个命令,改主席要是议员的不高兴值和最小,在相同的情况下,要是自己的头发变灰的数量尽可能的少。
让你求出通过哪n个命令,使得该主席的头发变灰的数量最多,在相同的情况下,输出使议员最不高兴值大的选择。
Input
5 3 2
5 6
5 8
1 3
Output
3 2 1
题意分析(我对于题意的理解可能会比较啰嗦,有点绕,但应该会容易懂):
这是一道非常奇葩的问题,主要的背景是源于主席和学生代表之间的阶级矛盾,学生代表大概是希望主席能够尽早下台,于是各种刁难,学校总共下达了n个命令,首先经过由学生代表进行的第一层审核通过p个,然后由主席接受k个命令(即去掉p-k个命令)。
首先定义两个量 ai,bi代表某个任务的两个值,ai为主席通过命令后头发变灰的数量,bi为如果主席不通过议员的不高兴值;
- 对于学生代表而言:他们通过的这p个命令,是主席最不想接受的,是使主席头发变灰的数目更多的命令,(主席)不接受会让议员不高兴值更高的命令
即 ai越大,bi越大;
他们通过的这些命令要尽可能少的包含主席最不想通过的命令(看似两方在统一战线,其实如果议员通过这些命令,主席也有权利不通过(此时我想到一个比较极端的反例:如果有一个使主席头发变灰数量极多,拒绝会使议员不高兴程度极高的命令,主席会不会愿意接受?学生代表会不会让其通过?这可能需要考虑一个顺序问题))
简单来说,议员通过的命令会让主席陷入两难境地。 - 对于主席而言:从学生代表通过的p个命令中,要通过k个命令,也就是去掉p-k个命令,主席想去掉的是(我认为这两个因素有一个优先级顺序,议员的不高兴值要优先考虑)使议员不高兴值少的,并且使主席头发变灰数量多的命令。
即 ai越大,bi越小;
如果读完上述解析对该题已经略有感悟,那么下面化简得思路应该就显得清晰很多了(如果上述思路没有看懂那就多读几遍题,脑子清楚后再看上面的解析,我觉得还是可以理解的,一定要有耐心)
首先需要知道对于每一个选择(p命令),该主席一定是把bi值小的p-k个不接受,如果有bi有相同的,则尽可能使自己头发变灰的数量最小即ai值尽量大。
Step1:按bi:从小到大排列,ai从大到小排列;前p-k个无论a有多大,如果选他都没有用,a不能发挥作用,所以只能在后面的n-(p-k)个里选。
Step2:按ai从大到小排列,bi大到小排列;选择前k个,使得sumai最大,bi的最小值也最大(因为要保证主席需要接受它)。
Step3:保证了k个命令的sumai最大后,剩下的任务就是保证p-k个命令的sumbi最大了再次像step1那样排序,此时注意在a、b相同时应把已经选在k个中的尽量靠前,然后依次输出后面的命令下标就够了
这个题的思路比较难懂,实现起来也相当麻烦
#define maxn 100005
using namespace std;
int n,m,p,k,ans;
int vis[maxn];
struct Node
{
int a,b,id;
int state;
}node[maxn];
bool cmp1(const Node&x1,const Node&x2)
{
if(x1.b!=x2.b) return x1.b<x2.b;
if(x1.a!=x2.a) return x1.a>x2.a;
return x1.state<x2.state; //最后按state由小到大排序 后面给出解释
}
bool cmp2(const Node&x1,const Node&x2)
{
if(x1.a!=x2.a) return x1.a>x2.a;
return x1.b>x2.b;
}
int main()
{
int i,j,cnt,num;
while(~scanf("%d%d%d",&n,&p,&k))
{
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)
{
scanf("%d%d",&node[i].a,&node[i].b);
node[i].id=i;
node[i].state=0;
}
sort(node+1,node+n+1,cmp1);
for(i=1;i<=p-k;i++)
{
node[i].state=1;
}
sort(node+1,node+n+1,cmp2);
cnt=0;
for(i=1;cnt<k;i++)
{
if(!node[i].state)
{
cnt++;
vis[node[i].id]=1;
node[i].state=2; // 将state的值修改
if(cnt==1) printf("%d",node[i].id);
else printf(" %d",node[i].id);
}
}
sort(node+1,node+n+1,cmp1);
cnt=num=0;
for(i=n;num<p-k;i--) {
if(cnt>=k)
{
num++;
printf(" %d",node[i].id);
}
if(vis[node[i].id]) cnt++;
}
printf("\n");
}
return 0;
}
说实话前几周学贪心真的比较水,很多题目似懂非懂,有的大致了解一下把代码理解一下再复制就过去了,当自己真正花几个小时仔细研究,哪怕只是一个题目,收获也是极大的,刚刚入门对于题目理解和进行实现感到困难也是常有的事,但只要坚持,耐心的多去看多去想,肯定会有豁然开朗、柳暗花明的一天。