返回顶部

洛谷 P6851 onu (贪心,模拟)

  • 题意:C和D打牌,每张牌有花色和点数,小D刚开始的分数为\(v\),不管输还是赢,只要小D出了牌(花色必须相同),就能得到那张牌点数的分数,若是赢了(点数不小于D的牌),他可以另外加\(c\)分,输了就要扣\(c\)分,现在D知道了C的出牌情况,问他最多能拿多少分,并输出出牌情况.

  • 题解:首先,假如他两的牌花色不同,那么D一定打不出牌,只能白白\(-=c\),否则我们要尽可能的出多的牌,并且要赢得多,贪心策略是,用D的最大的牌去打掉C最大的牌,如果C同种颜色最小的牌都比D最大的大,那么只能随便打一张骗一点分并且把C的最大的牌消耗掉,实现起来挺复杂的,具体看代码吧.

  • 代码:

    struct misaka{
    	int col;
    	int val;
    	int id;
    	bool operator < (const misaka &mikoto) const{
    		return val<mikoto.val;
    	}
    	bool operator > (const misaka &mikoto) const{
    		return val>mikoto.val;
    	}
    }a[N],b[N];
    
    int n,m,c;
    ll v;
    multiset<misaka> V[N];
    int ans[N];
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>n>>m>>c>>v;
        for(int i=1;i<=n;++i){
        	cin>>a[i].col>>a[i].val;
        	a[i].val=-a[i].val;    //存负数方便我们后面进行二分查找
        	a[i].id=i;
        }
        for(int i=1;i<=m;++i){
        	ans[i]=-1;
        	cin>>b[i].col>>b[i].val;
        	b[i].val=-b[i].val;    //存负数方便我们后面进行二分查找
        	b[i].id=i;
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=m;++i){
        	V[b[i].col].insert(b[i]);      //记录小C每种颜色的点数,set会对点数自动排序
        }
        for(int i=1;i<=n;++i){
        	if(V[a[i].col].empty()) continue;
        	v-=a[i].val;
        	auto p=V[a[i].col].lower_bound(a[i]);  //因为全是负数,找小C当前颜色牌中小于小D的牌
        	if(p==V[a[i].col].end()){              //小D最大的牌打不过小C最小的牌
        		v-=c;
        		p=V[a[i].col].begin();            //随便出一张
        	}
        	else v+=c;
        	//auto t=*p;
        	ans[p->id]=a[i].id;                //记录ans
        	V[a[i].col].erase(p);              //弹出小C的一张牌
        }
        for(int i=1;i<=m;++i){
        	if(ans[i]==-1) v-=c;
        }
        cout<<v<<endl;
        for(int i=1;i<=m;++i){
        	cout<<ans[i]<<endl;
        }
    
        return 0;
    }
    
posted @ 2020-10-21 20:34  _Kolibri  阅读(129)  评论(0)    收藏  举报