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);
}