考察知识:模拟
算法难度:X 实现难度:X+
分析:按照题目的要求模拟就可以了,只是要考虑严谨,还要看懂题目
代码:
#include
int cost,rest,store,fail;
int main(){
for(int i=1;i<=12;i++){
scanf("%d",&cost);
rest+=(300-cost);
if(rest<0) {fail=i;break;}
else store+=120*(rest/100),rest%=100;
}
if(fail) printf("%d\n",-fail);
else printf("%d\n",store+rest);
return 0;
}
考察知识:二叉堆,模拟,贪心
算法难度:XX 实现难度:XX+
分析:
直接每次合并最小的两堆就可以了
还是那句话:当年不能用STL,所以我们还是按当年的要求来吧(不用优先队列)
用priority_queue很方便,就不贴代码了,下面的代码是用二叉堆实现的,注释里面讲述了使用方法,你需要有二叉树的的知识储备才容易读懂
代码:
#include
#include
const int maxn=10005;
int heap[maxn],np,n,t,sum;//维护一个小根堆
/*heap是储存二叉树的一个数组,2*i,2*i+1是i的
左右儿子,保证这棵二叉树父亲的权值不比儿子大*/
void add(int x){//添加元素
heap[++np]=x;//把np理解为数组的指针-指向末尾元素
for(int i=np,j=i/2;j>0;i=j,j/=2){//调整二叉树的节点权值
if(heap[j]>heap[i]) std::swap(heap[i],heap[j]);
else break;
}
}
void del(){//删除最小元素
heap[1]=heap[np--];//把根赋最后节点的值,删除最后节点
for(int i=1,j=2*i;j<=np;i=j,j*=2){//调整二叉树
if(jheap[j]) std::swap(heap[i],heap[j]);
else break;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&t),add(t);
while(--n){
t=heap[1],del();
t+=heap[1],del();
add(t),sum+=t;
}
printf("%d\n",sum);
return 0;
}
考察知识:序列型动态规划
算法难度:XX+ 实现难度:XX+
分析:
其实就是求一次最长上升子序列,然后反着求一次最长下降子序列,然后找最高点就可以了
代码:
#include
#include
int n,a[105],f1[105],f2[105],mx;
//f1(i)=max(f1(j))+1||1<=j=1;i--)
for(int j=n;j>i;j--) if(a[i]>a[j])
f2[i]=std::max(f2[i],f2[j]+1);
for(int i=1;i<=n;i++) mx=std::max(mx,f1[i]+f2[i]-1);
printf("%d\n",n-mx);
return 0;
}
考察知识:搜索+剪枝,数学,高斯消元
算法难度:XXXX 实现难度:XXXX
分析:这道题搜索并不难写,但是剪枝的程度决定了你能得多少分
我第一次花了两个小时写(调bug)完了第一份程序,如果不剪枝,时间复杂度为:,可是我加了剪枝也才20分,然后我修改了一下,时间复杂度开了个根号,然后就可以90分了
下面先介绍90分方法:
首先是命名方式:
add[i]表示第i+1为应该向第i为进一
剪枝:1.如果当前某个字母必须赋某个值而这个值已经被使用,剪枝 2.如果某个字母已经被赋值了,就不用再枚举其可能的值了
其实剪枝并不是很多,也不是很复杂,只不过细节还是很多,很容易写错,情况要分类讨论(我找bug又花了一个小时T_T)
所以忠告大家一定要加强静态查错,考虑每一个可能出错的地方
代码:
#include
#include
int n,mp[30],used[30],add[30],got_ans;
char A[30],B[30],C[30];
void dfs(int row,int cur){
if(row<0){
if(!add[row+1])//第一位不该有进位
for(int i=0;i=0) {dfs(row,1);return;}//如果字母已经被赋值
for(int i=0;i=0){//如果字母二已被赋值,分类讨论
int t=mp[idA]+mp[idB]+add[row+1];
add[row]=t/n;//注意进位
if(mp[idC]>=0&&t%n==mp[idC]){//如果字母三已被赋值
dfs(row-1,0);
add[row]=0;
}
else if(mp[idC]<0&&used[t%n]==0){//根据字母一二可以确定字母三
mp[idC]=t%n,used[t%n]=1;
dfs(row-1,0);
mp[idC]=-1,add[row]=used[t%n]=0;
}
return;
}
int t;
for(int i=0;i=0&&(t%n==mp[idC])) dfs(row-1,0);
else if(mp[idC]<0&&used[t%n]==0){
mp[idC]=t%n,used[t%n]=1;
dfs(row-1,0);
mp[idC]=-1,used[t%n]=0;
}
mp[idB]=-1,add[row]=used[i]=0;
}
}
int main(){
scanf("%d%s%s%s",&n,A,B,C);
for(int i=0;i
其实只要再加一个剪枝就是100分算法了:3.考虑剩下的数,如果一列的三个数值都知道了那么就可以判断是否矛盾了
但是要考虑进位(true表示可以剪枝):
bool check(int limt){
int a,b,c;
for(int i=0;i<=limt;i++){
a=mp[A[i]-'A'],b=mp[B[i]-'A'],c=mp[C[i]-'A'];
//考虑要严谨
if(((a+b)%n)!=c&&((a+b+1)%n)!=c&&a>=0&&b>=0&&c>=0) return true;
}
return false;
}
最后修改一下:
if(got_ans||check(row)) return;
就可以AC了,那个之前超时的点500ms过,还是有点悬,但是比起以前速度快了60多倍
总结:
不知道你有没有注意到所有代码我都没有用using namespace std;
如果你不知道为什么的话——解释:如果因为你不加这句话如果某些地方报错你在它(函数等)前面加 std:: 一般就可以了
T1:刚学竞赛一个月就AC了,最近来做没有考虑严谨居然80分T_T
T2:第一次做出现在考试中,那时没有学优先队列,用map做的,100分,现在用二叉堆做,不小心写错一个字母(i写成了j)只得了20分T_T
T4:第一次同时搜索第一行,第二行,超时非常严重(有80分超时),搜索方法略微改变了一下,剪枝没有改就90了(但是找bug花了我很多时间),这说明了什么?:细节,严谨,前瞻性很重要。。。
总之这一年的题我做得非常浮躁,20+80+100+20=220,如果NOIP2018我还这么马虎,我就完了。。。