实验舱比赛19.3.10

A.prince

http://noi.ac/problem/242

题解:这道题80分的普通dp显然是秒切,但是要注意的是运算符的优先级,可能由于优先级造成问题。然后这类的进制问题要考虑每个数和二进制位的关系,所以可以想到对于每个进制位进行考虑,对于每个数,可以得到的最大贡献就是f[i]+1(1<<i&x)max,然后对于每个可行的进制位再回去看是否可以更新答案

错误:要注意进制从0开始

代码:

//80分作法 
/*#include<iostream>
#include<cstdio> 
#include<cstdlib>
using namespace std;
int d[1010101],b[1010101];//dp转移 
int ans=1,n;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i]);
    b[1]=1;
    for(int i=2;i<=n;i++){
        b[i]=1;
        for(int j=1;j<i;j++)
            if((d[i]&d[j])!=0)//wrong要注意运算符顺序 
                b[i]=max(b[i],b[j]+1);
    
    }
    for(int i=1;i<=n;i++)ans=max(ans,b[i]);
    printf("%d",ans);
    return 0;
}*/ 
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int f[40],n,te;
int main(){
    scanf("%d",&n);
    int x;
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        te=1;
        for(int j=0;j<=30;j++)
            if((1<<j)&x)te=max(te,f[j]+1);
        for(int j=0;j<=30;j++)
            if((1<<j)&x)f[j]=max(te,f[j]);
    }
    int ans=0;
    for(int i=1;i<=30;i++)ans=max(ans,f[i]);
    printf("%d",ans);
    return 0;
}
View Code

B.toy

http://noi.ac/problem/243

题解:这道题初读会感到害怕,但是这类题最好手推感受一下,由于我们知道每三个格子中有几个,所以可以知二推一(可以假设第0个格子为0,因为第二列第一个对应的正好就是0,1,2),所以我们只要考虑1的情况就可得到2,然后得到所有格子,所以可以发现最多就两种情况,我们分别枚举第1个格子为0或1的情况,就可以得出其他格子是否有物品,判断其是否只有0或一个,且第n+1个格子为0,若其中任意不满足就说明这种方法不可行

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int a[10101],b[10101];//b表示是否有放
int ans=2,n;
void check(){
    for(int i=2;i<=n+1;i++){
        b[i]=a[i-1]-b[i-1]-b[i-2];
        if(b[i]!=0&&b[i]!=1){ans--;break;}
        if(i==n+1&&b[i]!=0){ans--;break;}
    }
} 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    b[1]=0;
    check();
    b[1]=1;
    check();
    printf("%d",ans);
    return 0;
}
View Code

C.product

http://noi.ac/problem/244

题解:首先考虑贪心,因为要求的是个数多,所以一定是从小到大排序,然后取越多越好,所以考虑二分答案取前p个,于是将题目转化为一道搜索题(n<=50暗示搜索),来判定是否可以取完前p个,但是要注意要从大到小判断,因为我们判断的是全部是否都可以,所以如果先满足小的,被截掉之后可能无法满足大的。显然要考虑剪枝,第一枝可行性剪枝,如果拥有的全部长度减去不能用的(进过裁剪导致剩下的比第一个还小),第二支如果前一个和现在的一样大(正常要小),那么要从现在找到的这个开始枚举较优

错误:是否成立是有多条路径只要一条成立就好了,而不是一条路不成立就全不成立,所以不要贪懒

         要注意边界的设置,有限制范围的不能乱定,否则会加大范围(为空的部分也算进去了)

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int m,n;
int offer[1010],need[1010];
int used[1010];
int ans;
int waste,so,sn;
int dfs(int x,int la){//la进行剪枝,如果两个长度相等,一定要从现在这个开始 
    if(x<1)return true;
    if(so-waste<sn)return false;
    for(int i=la;i<=m;i++){
        if(used[i]>=need[x]){
            used[i]-=need[x];
            if(used[i]<need[1])waste+=used[i];//剩下的不能用了 
            if(need[x]==need[x-1]) {if(dfs(x-1,i))return true;}//wrong
            else {if(dfs(x-1,1))return true;}
            if(used[i]<need[1])waste-=used[i];
            used[i]+=need[x];
        }
    }
    return false;
}
int main(){
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&offer[i]);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&need[i]);
    sort(offer+1,offer+1+m);
    sort(need+1,need+1+n);
    int l=1,r=n;//wrong
    while(l<=r){//判断前p个是否能被完全拆开 
        int mid=(l+r)/2;
        waste=so=sn=0;//优化
        for(int i=1;i<=mid;i++)sn+=need[i];
        for(int i=1;i<=m;i++)so+=offer[i],used[i]=offer[i]; 
        if(dfs(mid,1)){
            ans=mid,l=mid+1;
        }
        else r=mid-1;
    }
    printf("%d",ans);
}
View Code

猜你喜欢

转载自www.cnblogs.com/linzeli/p/10551279.html
今日推荐