2018 10 01 校内模拟 贪心+状压+贪心

T1:卡牌游戏
cardgame.cpp
【题意】
L最近喜欢上了一个卡片游戏,游戏规则是:
2 个人一共拿 2n 张卡片,编号 1…2n,每个人 n 张,然后进行 n 轮出牌,每轮 2 个
人都打一张牌,,点数大的玩家每次获1分。
L可以预测到对方要打牌的顺序。
同时,L有一次机会选择了某个时间点,从那个时候开始,每回合点数少者获胜。

请你帮助 L获得最大的分数
【输入】
第一行是1个整数n,n是偶数
接下来n行表示,对手每次的出牌,根据这些数字,你一定知道了 L手上的牌的吧
【输出】
1个整数,表示L能获得最高分数

样例

Sample Input
4
1
8
4
3
Sample Output
3

分析:
(贪心)本题可以用田忌赛马的策略,赢不了时扔自己最小的牌。(如果没有那个转折点的话
考虑转折点的特殊,我们可以正着做一次,反着做一次,然后求max
可以证明贪心是正确的,详见本校神仙的证明:https://blog.csdn.net/zxyoi_dreamer/article/details/82917008

代码:

#include<cstdio>   
#include<cstring>   
#include<set>   
#include<algorithm>   
#include<iostream>   
using namespace std;   
#define maxn 50100   
set<int> l,r;   
bool flag[maxn*2];   
 int a[maxn],g[maxn],f[maxn];   
 int main()   
 {   
     freopen("cardgame.in","r",stdin);   
     freopen("cardgame.out","w",stdout);   
     int n,i,ans=0;   
     scanf("%d",&n);   
     memset(flag,true,sizeof(flag));   
     for (i=1;i<=n;i++)   
     {   
         scanf("%d",&a[i]);   
         flag[a[i]]=false;   
     }   
     for (i=1;i<=2*n;i++)    
      if(flag[i]){   
          r.insert(i);   
          l.insert(-i);     
      }    
     for (i=1;i<=n;i++){   
         set<int>::iterator it=r.upper_bound(a[i]);   
         if (it!=r.end()){   
             r.erase(*it);   
             f[i]=f[i-1]+1;   
         }   
         else f[i]=f[i-1];   
     }   
     for (i=n;i>=1;i--){   
         set<int>::iterator it=l.upper_bound(-a[i]);   
         if(it!=l.end()){   
             l.erase(*it);   
             g[i]=g[i+1]+1;   
         }   
         else g[i]=g[i+1];   
     }       
	 for(i=0;i<=n;i++)   
        ans=max(f[i]+g[i+1],ans);   
    printf("%d\n",ans);   
    return 0;   
}   

T2:偷书
grape.cpp
在L的书架上,有 N本精彩绝伦的书籍,每本书价值不菲。
M 是一个书籍爱好者,他对 L 的书籍早就垂涎三尺。最后他忍受不了诱惑,觉得去偷 L 的
书,为了迅速完成这件事,同时他不希望 L 很快发现书籍少了,他决定偷书时,对于任意
连续的 k 本书,他最多选 B 本,最少选 A 本。现在他想知道怎么选出来的书本最后使得偷
的书籍的价值和,与剩下的书籍价值和,差值最大。

【 Input】
第一行四个整数 n,k,a,b
一行 N 个整数表示每本书的价值
【 Output】
一个整数表示答案
【 Sample Input】
2 1 0 1
2 -2
【 Sample Output】
4
【 Hint】
得到第一本书 得到的价值和是 2
剩余的价值和是-2
差值为 4
【数据规模】
对于 20%:n<=10
对于另外 20%:a=0,b=k
对于 100%:n<=1000,0<=a<=b<=k<=10 k <= n 所有书籍的价值的绝对值<=10^9

分析:
k<=10 考虑状态压缩
用f[i][s]表示前i个人,状态为s的最大值
详见代码:

#include<bits/stdc++.h> 
using namespace std;   
const int maxn=1000;   
int score[maxn],N,K,B,A,nums[1024];   
long long fmax[maxn][1024],sum;   
void getn(){   
     for(int i=0;i<1024;i++)   
     {   
         int t=i,num=0;   
         while(t){   
             if(t&1)num++;   
             t/=2;   
         }   
         nums[i]=num;   
     }   
 }   
 bool judge(int a){   
     return !(nums[a]<A||nums[a]>B);   
 }   
 int main(){  
     scanf("%d%d%d%d",&N,&K,&A,&B);   
     for(int i=1;i<=N;i++){scanf("%d",&score[i]);sum+=score[i];}   
     getn();   
     for(int k=0;k<(1<<K);k++)   
     {   
         int temp=k;   
         for(int i=0;i<K;i++)   
         {   
             if(k&(1<<i))fmax[K][k]+=score[K-i];   
         }   
     }   
     for(int i=K+1;i<=N;i++)   
         {   
        for(int k=0;k<(1<<K);k++)   
        {   
            if(!judge(k))continue;   
            int t1=k;   
            t1>>=1;   
            if(k&1){   
                int t2=t1+(1<<K-1);   
                fmax[i][k]=fmax[i-1][t2]+score[i];   
                if(judge(t1)){   
                    fmax[i][k]=max(fmax[i][k],fmax[i-1][t1]+score[i]);   
                }   
            }else{   
                int t2=t1+(1<<K-1);   
                fmax[i][k]=fmax[i-1][t1];   
                if(judge(t2)){   
                    fmax[i][k]=max(fmax[i][k],fmax[i-1][t2]);   
                }   
            }   
        }   
    }   
    long long finalmax=-100000000000000LL;   
       
    for(int k=0;k<(1<<K);k++)   
    {   
        if(judge(k))   
        {   
            finalmax=max(finalmax,fmax[N][k]);   
        }   
    }   
    long long ans=finalmax-sum+finalmax;   
    printf("%lld",ans);   
}   

T3: 购买书籍
buy.cpp
【描述】
L的书籍被 M偷了以后伤心欲绝,决定再购买一些回来,现在有 N 本书可以买,每
本书的价格是 a[i]元。
现在L总共有 M 元,以及 K 张优惠券。 对于每本书,如果使用一张优惠券,则可
以用b[i]的优惠价格购买。 注意每本书只能使用一张优惠券,只能购买一次。

L想知道自己最多可以购买几本书?
【 输入】
第一行三个整数 N, K, M
接下来 N 行,每行两个整数,表示 a[i]和 b [i]。
【 输出】
一个整数表示答案。
【 Sample Input】
4 1 7
3 2
2 2
8 1
4 3
【 Sample Output】
3
【解释】
选择第 1、 2、 3 本书,其中第3本使用优惠券。总共 5 元。
【数据规模】
对于 20%:N<=10
对于 50%:N<=100
对于另外 20%:K = 0
对于 100%:1 <= N <= 100000,0 <= K <= N,M <= 10^14,1 <= b[i] <= a[i] <= 10^9

分析:
50%:用背包dp解决
100%:贪心,考虑直接用完所有优惠劵,之后如果有更优的使用优惠劵的方案,就用原价买一个前面的,然后用优惠价买后面的
需要用堆维护a[i],b[i],a[i]-b[i];

代码:

#include <cstdio>   
#include <cstdlib>   
#include <cstring>   
#include <algorithm>   
#include <set>   
#include <iostream>   
using namespace std;   
set<pair<int, int> > s1, s2, s3;   int n, k, ans;    
int fr[1000001], sc[1000001], id[1000001];   
long long m;   
bool bysc  (int a, int b) { return sc[a] < sc[b]; }   
int main()   
{   
    freopen("buy.in", "r", stdin); freopen("buy.out", "w", stdout);   
    scanf("%d%d%lld", &n, &k, &m);   
    for (int i = 1; i <= n; i++) scanf("%d%d", &fr[i], &sc[i]);   
    for (int i = 1; i <= n; i++) id[i] = i;   
    sort(id + 1, id + n + 1, bysc);   
    long long now = 0; int ans = 0;   
    if (k == 0){   
        sort(fr + 1, fr + n + 1);   
        for (int i = 1; i <= n; i++) if (m >= fr[i]) { m -= fr[i]; ans++; }   
        cout << ans << endl;   
        return 0;   
    }   
    for (int i = 1; i <= k; i++){   
        now += sc[id[i]]; if (now > m) goto out; ++ans;   
        s1.insert(make_pair(fr[id[i]] - sc[id[i]], id[i]));   
    }   
    for (int i = k + 1; i <= n; i++){   
        s2.insert(make_pair(sc[id[i]], id[i]));   
        s3.insert(make_pair(fr[id[i]], id[i]));   
    }   
    while(s2.size()){   
        int f1 = s1.begin()->first + s2.begin()->first, f2 = s3.begin()->first;   
        if (f1 < f2){   
            now += f1; if (now > m) goto out; ++ans;   
            int t2 = s2.begin()->second;   
            s1.erase(s1.begin());    
            s1.insert(make_pair(fr[t2] - sc[t2], t2));   
            s2.erase(s2.begin()); s3.erase(s3.find(make_pair(fr[t2], t2))); 
  
        }   
        else{   
            now += f2; if (now > m) goto out; ++ans;   
            int t2 = s3.begin()->second;   
            s3.erase(s3.begin()); s2.erase(s2.find(make_pair(sc[t2], t2))); 
  
        }       
	}   
out:   
    printf("%d\n", ans);   
}   

猜你喜欢

转载自blog.csdn.net/qq_43346903/article/details/82956720