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; }
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; }
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); }
原文:https://www.cnblogs.com/linzeli/p/10551279.html