noip题目

题目:选择客栈
题解:
本来写了一个60pts的RMQ,但是发现正解非常的巧妙,f[x]表示i之前(不包括i)颜色为x能用的客栈的数量,c[x]表示离这种颜色最近的客栈位置,cnt[x]表示从开始到i这个位置x的数量是多少,代码简洁思路巧妙。
代码:

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
int f[55],c[55],n,k,p,i,cnt[55];
int main()
{
    int ans=0,last=0;
    scanf("%d%d%d",&n,&k,&p);
    for (i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if (y<=p) last=i;
        if (last>=c[x]) f[x]=cnt[x];
        ans+=f[x];
        c[x]=i;
        cnt[x]++;
    }
    printf("%d",ans);
}

题目:借教室
题解:
运用前缀和我考虑到了,但如果n这样试过去的话很不科学,所以这里运用二分,二分订单k表示订单k不符合条件,而k-1的订单都符合条件,总复杂度O(logn(n+m))
代码:

#include <cstdio>
#include <cstring>
#define N 1000005
using namespace std;
struct hh{int d,s,t,id;}dd[N];
int tmp[N],n,R[N];
bool check(int k)
{
    memset(tmp,0,sizeof(tmp));
    for (int i=1;i<=k;i++)
    {
        tmp[dd[i].s-1]+=dd[i].d;
        tmp[dd[i].t]-=dd[i].d;
    }
    for (int i=n-1;i>=1;i--) tmp[i]+=tmp[i+1];
    for (int i=1;i<=n;i++) if (tmp[i]+R[i]<0) return 0;
    return 1;
}
int main()
{
    int m,i;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
      scanf("%d",&R[i]);
    for (i=1;i<=m;i++) scanf("%d%d%d",&dd[i].d,&dd[i].s,&dd[i].t);
    int l=1,r=m+1;
    while (l<r)
    {
        int mid=(l+r)>>1;
        if (check(mid)) l=mid+1;
        else r=mid;
    }
    if (r==m+1) printf("0");
    else printf("-1\n%d",r);
}

题目:观光公交
题解:
1、我们要在收益人最多的边上加速 2、如果这辆车的到达时间 比 这个景点来的最晚客人时间 要早的话,我们就不算为收益,反正早到了还要等着。
然后是几个数组的设计
代码:

#include <cstdio>
#include <iostream>
#define N 1005
#define M 10005
using namespace std;
struct ck{int t,a,b;}pas[M];
int d[N],t[M],go[N],f[N],getto[N],maxx[N];
int main()
{
    int n,m,i,k,j;
    scanf("%d%d%d",&n,&m,&k);
    for (i=1;i<n;i++) scanf("%d",&d[i]);
    for (i=1;i<=m;i++)
    {
      scanf("%d%d%d",&pas[i].t,&pas[i].a,&pas[i].b);
      maxx[pas[i].a]=max(maxx[pas[i].a],pas[i].t);
      go[pas[i].b]++; 
    }
    getto[1]=0;
    for (i=2;i<=n;i++) getto[i]=max(getto[i-1],maxx[i-1])+d[i-1];
    while (k--)
    {
        f[n]=0;
        for (i=n;i>=2;i--)
          if (!d[i-1]) f[i-1]=0;//不能寻找收益人群 
          else{
            f[i-1]=go[i];//首先这条路对下一站下车的人肯定有意义 
            if (getto[i]>maxx[i]) f[i-1]+=f[i];//如果修改对后面有益 
          }
        int maxn=0,pos;
        for (i=1;i<=n;i++) 
          if (maxn<f[i]){maxn=f[i]; pos=i;}
        if (maxn==0) break;
        d[pos]--;
        for (i=2;i<=n;i++) getto[i]=max(getto[i-1],maxx[i-1])+d[i-1];
    }
    int ans=0;
    for (i=1;i<=m;i++) 
      ans+=getto[pas[i].b]-pas[i].t;
    printf("%d",ans);
}

题目:乌龟棋
题解:
1A满意。f[i][j][k][l]表示1剩i张,2剩j张…….
代码:

#include <cstdio>
#include <iostream>
using namespace std;
int f[45][45][45][45],a[360],b[5];
int main()
{
    int n,m,i,j,k,l;
    scanf("%d%d",&n,&m);
    for (i=0;i<n;i++)
      scanf("%d",&a[i]);
    for (i=1;i<=m;i++) 
    {
        int a;scanf("%d",&a);
        b[a]++;
    }
    f[b[1]][b[2]][b[3]][b[4]]=a[0];
    for (i=b[1];i>=0;i--)
      for (j=b[2];j>=0;j--)
        for (k=b[3];k>=0;k--)
          for (l=b[4];l>=0;l--)
          {
            int bu=(b[1]-i)+(b[2]-j)*2+(b[3]-k)*3+(b[4]-l)*4;
            if (i<b[1]) f[i][j][k][l]=max(f[i][j][k][l],f[i+1][j][k][l]+a[bu]);
            if (j<b[2]) f[i][j][k][l]=max(f[i][j][k][l],f[i][j+1][k][l]+a[bu]);
            if (k<b[3]) f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k+1][l]+a[bu]);
            if (l<b[4]) f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k][l+1]+a[bu]);
          } 
    printf("%d",f[0][0][0][0]);
}

题目:聪明的质检员
题解:
一上来正解?二分+前缀和~嘛嘛嘛~但是最大值1e9不行啊,一些乱七八糟的元素爆longlong啊,真是烦~
哇改了LL之后一定要改上%lld啊
一个很大的数:0x3f3f3f3f3f3f3f3f
代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#define INF 1e9
#define N 200005
#define LL long long
using namespace std;
struct hh{int l,r;}pe[N];
LL s,ww[N];
LL jd=0x3f3f3f3f3f3f3f3f;
int n,m,w[N],v[N],len[N];
bool check(int mid)
{
    int i;
    for (i=1;i<=n;i++)
    {
        ww[i]=ww[i-1];
        len[i]=len[i-1];
        if (w[i]>=mid) ww[i]+=v[i],len[i]++;
    }
    LL ans=0;
    for (i=1;i<=m;i++)
      ans+=(len[pe[i].r]-len[pe[i].l-1])*(ww[pe[i].r]-ww[pe[i].l-1]);
    if (s<ans)
    {
        if (ans-s<jd) jd=ans-s;
        return 1;
    }
    else
    {
        if (s-ans<jd) jd=s-ans;
        return 0;
    }  
}
int main()
{
    int l=INF,r=0,i;
    scanf("%d%d%lld",&n,&m,&s);
    for (i=1;i<=n;i++) scanf("%d%d",&w[i],&v[i]),l=min(l,w[i]),r=max(r,w[i]);
    for (i=1;i<=m;i++)
      scanf("%d%d",&pe[i].l,&pe[i].r);
    while (l<r)
    {
        int mid=(l+r)>>1;
        if (check(mid)) l=mid+1;
        else r=mid-1;
    }
    check(l);
    printf("%lld",jd);
}

题目:传染病控制
题意:一棵树每一层砍一条边,求与根连通的最小节点数
题解:
好的每次切掉重儿子是不对滴,请看优美菊花图( 有子树深度为2,一个根,剩下的全都是叶子)
这里写图片描述
正好学习一下vector的应用?
首先dfs建树找出每个点控制的点的个数和每个点的深度,还有就是深度为dep的点用一个vector存起来。然后以深度为关键字搜索,每一深度枚举要切断的点,将它控制的点的个数计入答案,然后套一个dfs…用来把这个点的所有子节点标记…到时候就不能切断它们了…
其实就是一个特别特别暴力需要强行枚举的题目啊。。。。。
代码:

#include <cstdio>
#include <iostream>
#include <vector>
#define N 700
#define INF 1e9
using namespace std;
int nxt[N],point[N],v[N],tot,son[N],h[N],f[N],maxx,ans=INF;
bool vis[N];
vector <int> s[N];
void addline(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int fa)
{
    h[x]=h[fa]+1;
    s[h[x]].push_back(x);
    son[x]=1;
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa)
      {
        dfs(v[i],x);
        son[x]+=son[v[i]];
      }
}
void bj(int x,bool flag)
{
    vis[x]=flag;
    for(int i=point[x];i;i=nxt[i])
      if(h[v[i]]>h[x])
        bj(v[i],flag);
}
void dfs1(int dep,int now)
{
    ans=min(ans,now);
    for (int i=0;i<s[dep].size();i++)
      if (!vis[s[dep][i]])
      {
        bj(s[dep][i],1);
        dfs1(dep+1,now-son[s[dep][i]]);
        bj(s[dep][i],0);
      }
}
int main()
{
    int n,p,i,j;
    scanf("%d%d",&n,&p);
    for (i=1;i<=p;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addline(x,y);
    }
    dfs(1,0);
    dfs1(2,n);
    printf("%d",ans);  
}

题目:篝火晚会
题解:
我真的炒鸡炒鸡讨厌优快云!!!
可以用贪心来做,如果知道原序列和目标序列,算算目标序列中有几个元素和原序列不同,就是答案了。要想办法构造目标序列。可以随便放一个数,然后在它的右边放上一个他希望相邻的数(有两个,所以要分两种情况)然后就可以按照这样的方法逐个构造。
现在构造出了目标序列,但它是个圈,所以要破环成链比较,但这样效率O(n^2),会超时。
其实,只需要知道有多少个元素和原序列相同,就能知道有几个元素不同了。
那么,对于某个目标序列里的元素a[i],如果我们比较的时候从目标序列i-a[i]+1的位置开始,就会和原序列相同,可以f[i-a[i]+1]++;(但要注意边界),然后取所有的n-f[i]的最小值。
代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#define N 50005
#define INF 1e9
using namespace std;
int a[N],b[N],t[N],f[N],ans=INF;
int main()
{
    int n,i;
    scanf("%d",&n);
    for (i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
    for (i=1;i<=n;i++)
      if (a[a[i]]!=i && b[a[i]]!=i || a[b[i]]!=i && b[b[i]]!=i) {printf("-1"); return 0;}
    t[1]=1; t[2]=a[1];
    for (i=3;i<=n;i++) 
      if (a[t[i-1]]==t[i-2]) t[i]=b[t[i-1]];
      else t[i]=a[t[i-1]];

   for (i=1;i<=n;i++)
     if (i-t[i]+1>0) f[i-t[i]+1]++;
     else f[n+i-t[i]+1]++;
    for (i=1;i<=n;i++)
      ans=min(ans,n-f[i]);

    memset(f,0,sizeof(f));
    t[1]=1; t[2]=b[1];
    for (i=3;i<=n;i++) 
      if (a[t[i-1]]==t[i-2]) t[i]=b[t[i-1]];
      else t[i]=a[t[i-1]];

   for (i=1;i<=n;i++)
     if (i-t[i]+1>0) f[i-t[i]+1]++;
     else f[n+i-t[i]+1]++;
    for (i=1;i<=n;i++)
      ans=min(ans,n-f[i]);
    printf("%d",ans);
}

题目:Hankson 的趣味题
题解:
一看题目就是数论某题
首先画个柿子这里写图片描述
可以枚举x为b1的因子,a1的倍数,然后起飞?
代码:

#include <cstdio>
using namespace std;
int gcd(int a,int b){if (b==0) return a;else return gcd(b,a%b);}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int a0,a1,b0,b1,i,ans=0;
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        for (i=1;i*i<=b1;i++)//不可以从a1枚举,因为a1*a1大于b1的话不能找到b1所有的因子
          if (b1%i==0)
            {
                if (i%a1==0 && gcd(i/a1,a0/a1)==1 && gcd(b1/i,b1/b0)==1) ans++;
                int ly=b1/i;
                if (ly!=i && ly%a1==0 && gcd(ly/a1,a0/a1)==1 && gcd(b1/ly,b1/b0)==1) ans++;
            }   
        printf("%d\n",ans);
    }
}

题目:奶牛的锻炼
题解:详见注释。
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int a[2005],f[2005][505];
int main()
{
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    //f[i][j]第i分钟疲倦程度为j的最大距离 
    memset(f,0x8f,sizeof(f));
    f[0][0]=0;
    for (i=1;i<=n;i++)
      for (j=0;j<=m;j++)
      {
        if (j==0) f[i][j]=max(f[i][j],max(f[i-1][j+1],f[i-1][j]));//上一分钟才开始休息 
        else {
            f[i][j]=max(f[i][j],f[i-1][j-1]+a[i]);
            f[i+j][0]=max(f[i+j][0],f[i-1][j+1]);//上一分钟休息了就休息到底吧 
          }
      }
    printf("%d",f[n][0]);
}

题目:新三国争霸
题解:
f[i]表示前i时间最少的花费,总的来说还是比较暴力的题目吧
代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define INF 1e9
using namespace std;
struct road{int x,y,z,id;}sd[5005];
int n,m,t,v,k,fa[305],r[305][305],f[55];
bool bx[5005][55];
int cmp(road a,road b){return a.z<b.z;}
int find(int x)
{
    if (fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
int work(int l,int r)
{
    int i,j,cnt=0,len=0;
    for (i=1;i<=n;i++) fa[i]=i;
    for (i=1;i<=m;i++)
    {
        bool fff=false;
        for (j=l;j<=r;j++) if (bx[sd[i].id][j]) {fff=true; break;}
        if (fff) continue;
        int a=find(sd[i].x),b=find(sd[i].y);
        if (a!=b){fa[a]=b;cnt++;len+=sd[i].z;if (cnt==n-1) return len;}
    }
    return INF;
}
int main()
{
    int i,j,p;
    scanf("%d%d%d%d%d",&n,&m,&t,&v,&k);
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d",&sd[i].x,&sd[i].y,&sd[i].z);
        r[sd[i].x][sd[i].y]=r[sd[i].y][sd[i].x]=i;
        sd[i].id=i;
    }
    sort(sd+1,sd+m+1,cmp);
    scanf("%d",&p);
    for (i=1;i<=p;i++)
    {
        int x,y,t1,t2;
        scanf("%d%d%d%d",&x,&y,&t1,&t2);
        int hh=r[x][y];
        for (j=t1;j<=t2;j++) bx[hh][j]=true;
    }
    memset(f,0x7f,sizeof(f));f[0]=0;
    for (i=1;i<=t;i++)
      for (j=0;j<i;j++)
        f[i]=min(f[i],f[j]+work(j+1,i)*(i-j)*v+k);
    printf("%d",f[t]);
}

to be continued……(luogu的下载次数被我用完了,那我就先放过luogu吧hhhhhh)

<think>好的,我现在要帮用户整理一份学习C++的博客清单,并制定一个规划表,帮助他们从入门到达到NOIP水平。首先,我需要确定用户的需求。用户是刚开始学习C++,目标是通过NOIP竞赛,所以需要覆盖从基础语法到高级算法的内容。 首先,我得分解学习路径。通常NOIP考察的内容包括基本语法、数据结构、算法、以及题目实战。所以博客清单应该分阶段,逐步深入。 第一阶段肯定是基础语法。用户需要了解变量、循环、条件语句、函数、数组这些。然后是STL的使用,比如vector、string、queue、stack等,这些在竞赛中非常常用,能节省很多时间。 接下来是基础算法,比如排序、查找、递归、分治、贪心、简单动态规划。这些是解决NOIP题目的基础,必须扎实。 第三阶段是数据结构进阶,比如链表、树、图、并查集、堆、哈希表。特别是树和图的相关算法,比如遍历、最短路径、最小生成树,这些在竞赛中经常出现。 第四阶段是高级算法,包括动态规划、搜索算法(DFS、BFS、剪枝)、数论、字符串处理(KMP、字典树)、高级图论算法。动态规划在NOIP中占比较大,需要重点讲解。 最后是综合实战,通过模拟题和历年真题来巩固所学知识,熟悉比赛题型和时间管理。 接下来,我需要为每个阶段推荐具体的博客资源。要确保这些资源是中文的、免费且质量较高的。比如C语言中文网、OI Wiki、阮行止的博客、Pecco的算法课等,这些资源比较权威,适合不同阶段的学习。 然后是规划表。用户可能需要一个为期4-5个月的计划,每周安排不同的主题。例如,前两周学习基础语法,接下来两周学习STL,然后逐步深入算法和数据结构。每个月复习和实战测试也很重要,帮助巩固知识。 需要注意用户可能有不同的学习速度,所以规划表要有一定的弹性,建议每天2-3小时的学习时间,保持持续学习。同时,提醒用户多做练习题,参加在线评测平台,如洛谷、Codeforces,这些对提升实战能力很有帮助。 最后,检查是否有遗漏的部分,比如调试技巧、代码优化,或者竞赛中的常见陷阱。确保推荐的博客内容覆盖这些方面,帮助用户全面准备NOIP。</think>我将为您整理一份C++学习路径及配套博客资源清单,并附上详细规划表。以下内容根据NOIP考纲要求设计,分阶段渐进式学习: --- ### 一、分阶段学习博客清单 #### 📚 阶段1:C++语法基础 1. **《C++快速入门指南》**(C语言中文网) - 环境搭建、变量类型、运算符、流程控制、函数 2. **《从零理解面向过程编程》**(菜鸟教程) - 数组、字符串、结构体、指针基础 3. **《C++标准输入输出全解析》**(洛谷日报) - 输入输出优化、流控制、格式处理技巧 #### 🔧 阶段2:STL与基础算法 1. **《STL容器完全指南》**(OI Wiki) - `vector`/`string`/`queue`/`stack`/`map`详解 2. **《十大排序算法动画解析》**(Hello算法) - 冒泡/选择/插入/归并/快排/堆排序对比 3. **《递归与分治实战20例》**(Codeforces教程) - 汉诺塔、斐波那契、快速幂实现 #### 🧠 阶段3:数据结构进阶 1. **《树形结构完全手册》**(AcWing博客) - 二叉树/二叉搜索树/堆/并查集实现 2. **《图论算法三部曲》**(Pecco的算法课) - DFS/BFS/最短路径/最小生成树 3. **《动态规划入门到精通》**(阮行止的博客) - 背包问题、线性DP、状态转移方程设计 #### ⚡ 阶段4:NOIP专项突破 1. **《竞赛数学技巧大全》**(洛谷日报) - 质数筛法、快速幂、模运算、组合数学 2. **《剪枝与优化实战指南》**(OI Wiki) - 记忆化搜索、双向BFS、启发式搜索 3. **《历年NOIP真题精讲》**(信息学奥赛一本通) - 2015-2022年真题解析 --- ### 二、学习规划表(总时长4-5个月) #### 📅 第1-2周:语法筑基 - 每天2小时学习基础语法 - 配套练习:洛谷「新手村」题目 - 重点掌握:循环结构、数组操作、函数编写 #### 📅 第3-4周:STL实战 - 重点练习:`vector`和`sort`的配合使用 - 典型题目:P1177(排序)、P1553(字符串处理) #### 📅 第5-6周:算法入门 - 每日刷3道递归/分治类题目 - 理解时间复杂度的计算方法 #### 📅 第7-8周:数据结构攻坚 - 专项训练:并查集(P1551)、堆(P1090) - 掌握树的前中后序遍历实现 #### 📅 第9-12周:动态规划与图论 - 重点突破:背包九讲、Floyd/Dijkstra算法 - 完成10道经典DP题目(如P1216数字三角形) #### 📅 第13-16周:综合实战 - 每周完成2套NOIP模拟赛 - 分析错题并撰写解题报告 - 学习对拍调试技巧 --- ### 三、关键学习建议 1. **代码实践**:每个知识点必须配合3-5道练习题 2. **调试能力**:学会使用`gdb`或IDE调试工具 3. **复杂度控制**:保证算法能通过$10^5$量级数据 4. **模考策略**:训练每题最多25分钟的解题速度 ### 四、推荐练习平台 - **洛谷**(www.luogu.com.cn) - **AcWing**(www.acwing.com) - **Codeforces**(codeforces.com) 附学习路线图: ```mermaid graph TD A[语法基础] --> B[STL容器] B --> C[排序/查找] C --> D[递归/分治] D --> E[线性数据结构] E --> F[树形结构] F --> G[图论算法] G --> H[动态规划] H --> I[数学基础] I --> J[综合实战] ``` 建议每天保持2-3小时有效学习时间,每周安排半天进行错题复习。遇到难点时,可参考《算法竞赛进阶指南》纸质书辅助学习。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值