NOIP2016提高组DAY2题解

版权声明:欢迎转载+原文章地址~ https://blog.csdn.net/Hi_KER/article/details/83537456

 

T1:组合数问题

考察知识:数学,记忆化

算法难度:XX+ 实现难度:XX+

分析:

f(n,m) 表示0<=i<=n,j<=min(i,m)中 k|C_i^j 的数的个数

状态转移:f(i,j)=f(i-1,j)+cnt,其中cnt表示\sum_{j<=i \,\,k|C_i^j}1

预处理之后,对于每个输入我们直接输出答案f(n,min(n,m))即可

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[2018][2018],ans[2018][2018],n,m,k,t;
int main(){
    scanf("%d%d",&t,&k);
    ans[0][0]=0;
    for(int i=0;i<=2000;i++) a[i][i]=a[i][0]=1;
    for(int i=1;i<=2000;i++){
        int cnt=0;
        for(int j=1;j<i;j++){
            a[i][j]=a[i-1][j]+a[i-1][j-1];
            a[i][j]%=k;
            if(!a[i][j]) cnt++;
            ans[i][j]=ans[i-1][j]+cnt;
        }
        ans[i][i]=ans[i-1][i-1]+cnt;
    }
    while(t--){
        scanf("%d%d",&n,&m);
        if(m>n) m=n;
        printf("%d\n",ans[n][m]);
    }
    return 0;
}

T2:蚯蚓

考察知识:队列,模拟

算法难度:XXX+ 实现难度:XXX+

分析:

不想多说:

  • 每次蚯蚓增长可以用一个变量表示,不必直接模拟
  • 这道题可以用优先队列或二叉堆实现,代码简单,但是会超时
  • 分析后发现其实队列本身具有单调性,我们分别维护3个保持单调性的队列即可

代码:

85代码:(二叉堆实现)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=15000000;
#define ui unsigned int
int n,m,q,u,v,t,k;
int heap[maxn],np;
void push(int k){
    heap[++np]=k;
    int x=np,y=np/2;
    while(y>0){
        if(heap[y]<heap[x]) swap(heap[x],heap[y]),x=y,y/=2;
        else break;
    }
}
void pop(){
    heap[1]=heap[np--];
    int x=1,y=2;
    while(y<=np){
        if(y<np&&heap[y]<=heap[y+1]) y++;
        if(heap[x]<heap[y]) swap(heap[x],heap[y]),x=y,y*=2;
        else break; 
    }
}
int main(){
    scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
    for(int i=1;i<=n;i++)
        scanf("%d",&k),push(k);
    for(int i=0;i<m;i++){
        k=heap[1]+i*q,pop();
        if((i+1)%t==0) printf("%d ",k);
        int tmp=(long long)u*k/v;
        push(tmp-q*i-q),push(k-tmp-q*i-q);
    }
    printf("\n");
    for(int i=1;i<=m+n;i++){
        if(i%t==0) printf("%d ",heap[1]+q*m);
        pop();
    }
    return 0;
}

100分代码:(多个队列维护实现)

#include<cstdio>
#include<cstring>
#include<queue>
#include<cctype>
#include<algorithm>
using namespace std;
const int maxn=10000000;
int n,m,q,u,v,t,k,pos,mx;
int que[3][maxn],front[3],rear[3];
#define push(i,var) que[i][rear[i]++]=(var)
#define pop(i) front[i]++
#define front(i) que[i][front[i]]
bool empty(int i){
    return rear[i]==front[i];
}
bool cmp(int x,int y){return x>y;}
int out[15];
void scan(int &sca){
    char ch=getchar();  
    while(!isdigit(ch))ch=getchar();  
    sca=0;  
    while(isdigit(ch)) sca=sca*10+ch-'0',ch=getchar();  
}
void print(int pnt){
    int p=0;
    if(pnt==0) {putchar('0');return;}  
    else while(pnt){out[p++]=pnt%10;pnt/=10;}
    for(int j=p-1;j>=0;j--) putchar('0'+out[j]);  
}
int main(){
//	freopen("in.in","r",stdin);
//	freopen("ans2.out","w",stdout);
    scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
    for(int i=0;i<n;i++) scan(que[0][i]);
    sort(que[0],que[0]+n,cmp);
    rear[0]=n;
    for(int i=0;i<m;i++){
        for(int j=0;j<3;j++) if(!empty(j)) {pos=j;break;}
        if(!empty(0)&&front(0)>front(pos)) pos=0;
        if(!empty(1)&&front(1)>front(pos)) pos=1;
        if(!empty(2)&&front(2)>front(pos)) pos=2;
        k=front(pos)+i*q,pop(pos);
        if((i+1)%t==0) print(k),putchar(' ');
        int tmp=(long long)k*u/v;
        push(1,tmp-q*i-q);
        push(2,k-tmp-q*i-q);
    }
    putchar('\n');
    for(int i=1;i<=n+m;i++){
        mx=-0x7ffffffa;
        for(int j=0;j<3;j++) if(!empty(j)&&front(j)>mx)
            mx=front(j),pos=j;
        pop(pos);
        if(i%t==0) print(mx+m*q),putchar(' ');
    }
    return 0;
}

T3:愤怒的小鸟

考察知识:状态压缩,搜索

算法难度:XXXX 实现难度:XXX+

分析:

我之前尝试用搜索解决,但是我的方法只有90分,超时的两个点每个需要2分钟才出答案,于是我放弃了用搜索解决,参考了P2831 愤怒的小鸟 题解(特别是用dp算法的第一篇题解)中的状态压缩的解法。

定义状态方程:f(A)表示消灭集合A中所有的猪需要的最少小鸟数

状态转移方程:

f(A\cup B_{ij})=min(f(A\cup B_{ij}),f(A)+1),其中B_{ij}表示按消灭猪 i,j 的轨迹可以消灭的猪的集合

f(A\cup \{i\})=min(f(A\cup \{i\}),f(A)+1),其中\{i\}表示一只猪 i 的集合

边界:f(\varnothing )=0

时间复杂度:O(Tn^22^n),面对大数据会超时

优化:

我们发现状态转移的顺序不影响结果,所以我们让转移顺序唯一,这样可以减少转移次数

bit\_[A]表示A \cap\{i\}=\varnothing的最小 i 值

对于每次集合 A 的转移,我们转移 B[bit\_[A]][i]\,\,\,{i}\in U 即可

这样每次转移的时间复杂度从O(n^2)降到了O(n),总时间复杂度为:O(Tn2^n)

代码:(个人觉得比较容易看懂,就没有加注释)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=0.0000001;
double x[20],y[20];
int f[1<<18],bit_[1<<18],pig[20][20],n,m,T;
void solve(){
    memset(f,1,sizeof(f));
    memset(pig,0,sizeof(pig));
    f[0]=0;
    for(int i=0;i<n;i++)
    for(int j=i+1;j<n;j++) if(fabs(x[i]-x[j])>eps){
        double a_=(y[i]*x[j]-x[i]*y[j])/((x[i]*x[j])*(x[i]-x[j]));
        if(a_>0.0) continue;
        double b_=(x[j]*x[j]*y[i]-x[i]*x[i]*y[j])/((x[i]*x[j])*(x[j]-x[i]));
        for(int t=0;t<n;t++) if(fabs(y[t]-x[t]*(a_*x[t]+b_))<eps)
          pig[i][j]+=1<<t;
        pig[j][i]=pig[i][j];
    }
    for(int i=0;i<(1<<n);i++){
        int t=bit_[i];
        f[i|(1<<t)]=min(f[i|(1<<t)],f[i]+1);
        for(int j=0;j<n;j++)
          f[i|pig[t][j]]=min(f[i|pig[t][j]],f[i]+1);
    } printf("%d\n",f[(1<<n)-1]);
}
int main(){
    for(int i=(1<<18)-1;i>=0;i--){
        int t=0;
        while(t<18&&i&(1<<t)) t++;
        bit_[i]=t;
    }
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) scanf("%lf%lf",x+i,y+i);
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/83537456
今日推荐