贪心问题:主席和学生代表的势力竞争(详解包含整个思路历程)

描述:
有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为如果主席不通过议员的不高兴值;

  1. 对于学生代表而言:他们通过的这p个命令,是主席最不想接受的,是使主席头发变灰的数目更多的命令,(主席)不接受会让议员不高兴值更高的命令
    即 ai越大,bi越大;
    他们通过的这些命令要尽可能少的包含主席最不想通过的命令(看似两方在统一战线,其实如果议员通过这些命令,主席也有权利不通过(此时我想到一个比较极端的反例:如果有一个使主席头发变灰数量极多,拒绝会使议员不高兴程度极高的命令,主席会不会愿意接受?学生代表会不会让其通过?这可能需要考虑一个顺序问题))
    简单来说,议员通过的命令会让主席陷入两难境地。
  2. 对于主席而言:从学生代表通过的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;
}

说实话前几周学贪心真的比较水,很多题目似懂非懂,有的大致了解一下把代码理解一下再复制就过去了,当自己真正花几个小时仔细研究,哪怕只是一个题目,收获也是极大的,刚刚入门对于题目理解和进行实现感到困难也是常有的事,但只要坚持,耐心的多去看多去想,肯定会有豁然开朗、柳暗花明的一天。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值