1.巧排扑克牌
直接手推
#include <iostream>
using namespace std;
int main(){
//注意正面朝下
cout<<"7, A, Q, 2, 8, 3, J, 4, 9, 5, K, 6, 10";
return 0;
}
2.质数拆分
注意审题!是若干个两两不同,不是两个
即找出2~2019中和为2019的n个数一共有几种组合(非排列)
类似于01背包
#include <iostream>
using namespace std;
const int N=2500;
bool st[N];
int prime[N],cnt=1;
long long dp[N][N];
void isprime(){
for(int i=2;i<=2019;i++){
if(!st[i])prime[cnt++]=i;
for(int j=1;prime[j]<=2019/i&&j<cnt;j++){
st[i*prime[j]]=true;
//这里注意j从1开始 %0会报错
if(i%prime[j]==0)break;
}
}
}
int main()
{
//从i个数中,使总和为j(2019)
isprime();
dp[0][0]=1;
//涉及到[j-1],所以j从1开始
for (int i = 1; i < cnt; i ++)
for (int j = 0; j <= 2019; j ++)
{
dp[i][j] = dp[i - 1][j];
if(j >= prime[i]) dp[i][j] += dp[i - 1][j - prime[i]];
}
printf("%lld",dp[cnt-1][2019]);
return 0;
}
优化为一维
#include <iostream>
using namespace std;
const int N=2040;
int prime[N],cnt=0;
bool st[N];
long long dp[N];
void isprime(){
//两个循环的等号必须加上
for(int i=2;i<=N;i++){
if(!st[i])prime[++cnt]=i;
for(int j=1;prime[j]<=N/i;j++){
st[prime[j]*i]=true;
if(i%prime[j]==0)break;
}
}
}
int main(){
//dp[i][j] 在前i个中选使得和为j
isprime();
dp[0]=1;
for(int i=1;i<=cnt;i++){
for(int j=2019;j>=0;j--){
if(j>=prime[i])dp[j]+=dp[j-prime[i]];
}
}
printf("%lld",dp[2019]);
return 0;
}
3.日志统计
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+10;
struct Node{
int ts,id;
bool operator <(const Node& N)const{
return ts<N.ts;
}
}node[N];
int haash[N];
bool st[N];
int main()
{
int n,d,k;
scanf("%d%d%d",&n,&d,&k);
for(int i=0;i<n;i++){
scanf("%d%d",&node[i].ts,&node[i].id);
}
sort(node,node+n);
for(int l=0,r=0;r<n;r++){
haash[node[r].id]++;
while(node[r].ts-node[l].ts>=d){
haash[node[l].id]--;
l++;
}
if(haash[node[r].id]>=k)st[node[r].id]=true;
}
for(int i=0;i<N;i++){
if(st[i])printf("%d\n",i);
}
return 0;
}
4.递增三元组
Ai<Bi<Ci
根据N,判断时间复杂度大概为O(n)或O(nlogn)
循环B,找到小于B[i]的A有几个,大于B的C有几个
法一:前缀和 O(n)
cnt[i]表示A中i出现的次数
s[i]=cnt[0]~cnt[i] 表示0~i在A中的出现次数
#include <iostream>
using namespace std;
const int N=1e5+10;
int a[N],b[N],c[N];
int cnt1[N],cnt2[N];
int s1[N],s2[N];
int main(){
int n;
scanf("%d",&n);
int w=3;
int min1=N,max1=0;
while(w){
for(int i=1;i<=n;i++){
if(w==3){
scanf("%d",&a[i]);
cnt1[a[i]]++;
min1=min(min1,a[i]);
max1=max(max1,a[i]);
}
if(w==2){
scanf("%d",&b[i]);
min1=min(min1,b[i]);
max1=max(max1,b[i]);
}
if(w==1){
scanf("%d",&c[i]);
cnt2[c[i]]++;
min1=min(min1,c[i]);
max1=max(max1,c[i]);
}
}
w--;
}
//注意min,max必须覆盖全部数字
for(int i=min1;i<=max1;i++){
s1[i]=s1[i-1]+cnt1[i];
}
for(int i=min1;i<=max1;i++){
s2[i]=s2[i-1]+cnt2[i];
}
long long ans=0;
for(int i=1;i<=n;i++){
int l=s1[b[i]-1];
int r=s2[max1]-s2[b[i]];
//这里必须进行强转
if(l>0&&r>0)ans+=(long long)l*r;
}
printf("%lld",ans);
return 0;
}
法二:排序加二分 O(nlogn)
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+100;
int a[N],b[N],c[N];
int n;
int searchl(int x){
int l=0,r=n-1;
while(l<r){
int mid=(l+r+1)/2;
if(a[mid]<x)l=mid;
else r=mid-1;
}
if(a[l]<x)return l;
else return -1;
}
int searchr(int x){
int l=0,r=n-1;
while(l<r){
int mid=(l+r)/2;
if(c[mid]>x)r=mid;
else l=mid+1;
}
if(c[l]>x)return l;
else return -1;
}
int main(){
scanf("%d",&n);
int w=3;
long long ans=0;
while(w--){
for(int i=0;i<n;i++){
if(w==2)scanf("%d",&a[i]);
if(w==1)scanf("%d",&b[i]);
if(w==0)scanf("%d",&c[i]);
}
}
//二分的前提是有序
sort(a,a+n);
sort(b,b+n);
sort(c,c+n);
for(int i=0;i<n;i++){
int l=searchl(b[i]);
int r=searchr(b[i]);
if(l!=-1&&r!=-1){
//不能写成(long long)((l+1)*(n-r));
ans+=(long long)(l+1)*(n-r);
}
}
printf("%lld",ans);
return 0;
}
法三:双指针
最后一个样例会运行超时
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5+10;
int num[3][N];
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < 3; ++i)
for(int j = 1; j <= n; ++j)
scanf("%d", &num[i][j]);
for(int i = 0; i < 3; ++i)
sort(num[i]+1, num[i]+n+1);
LL ans = 0;
//枚举B,寻找A满足的个数以及C满足的个数相乘
int a = 1, c = 1;
for(int i = 1; i <= n; ++i) {
int key = num[1][i];
while(a<=n && num[0][a] < key) a++;
while(c<=n && num[2][c] <= key) c++;
ans += (LL)(a-1)*(n-c+1);
}
cout<<ans<<endl;
return 0;
}
5.外卖店优先级
坑点
首先 设定的数值就很有意思
value<=3的时候 从缓冲队列释放 value>5的时候 放入缓冲队列
每次有外卖的时候加2分
那如果我value=3 不是刚好卡在中间??
这也是优先级减小和优先级增加的先后顺序 会对结果产生影响的原因
详细解释
假设当前节点为id 且当前时间点刚开始的时候 优先级为3
且节点id之前在缓冲队列中 满足f[id]=1
1.正常流程
因为时间点刚开始的时候 优先级为3
节点id从队列中释放f[id]=0
然后有外卖订单 优先级变为5 f[id]仍保持为从1变成0的结果
2.若先加分再判断
那么相当于时间点刚开始的时候
我们就有了3+2=5的优先级
f[id]=1的结果不变
也就是造成了影响!!!!!
所以这道题目一定要想清楚
判断是否出队是在时间点刚开始的时候!!
优先级增加是在时间点结束的时候!!!
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+100;
struct Node{
int ts,id;
bool operator <(const Node &node)const{
return id<node.id||id==node.id&&ts<node.ts;
}
}node[N];
bool st[N];//是否在缓存
int priority[N];//优先级
int pre[N];//上一次时间
int main()
{
int n,m,t;
long long ans=0;
scanf("%d%d%d",&n,&m,&t);
for(int i=0;i<m;i++){
scanf("%d%d",&node[i].ts,&node[i].id);
}
sort(node,node+m);
for(int i=0;i<m;i++){
int ts=node[i].ts;
int id=node[i].id;
//printf("id=%d\n",id);
//printf("ts=%d\n",ts);
//注意处理的顺序
if(pre[id]!=ts)priority[id]-=ts-pre[id]-1;
priority[id]=max(0,priority[id]);
if(priority[id]<=3)st[id]=false;
priority[id]+=2;
if(priority[id]>5)st[id]=true;
pre[id]=ts;
}
//printf("pre1=%d\n",pre[1]);
for(int i=1;i<=n;i++){
if(pre[i]<t){
//注意这里一定会少所以多减一个1
priority[i]-=t-pre[i];
}
if(priority[i]<=3)st[i]=false;
}
for(int i=1;i<=n;i++){
if(st[i]){
//printf("%d\n",i);
ans++;
}
}
printf("%d",ans);
return 0;
}