zoukankan      html  css  js  c++  java
  • NOIP2010题解

    所有题目链接均来自洛谷

    T1机器翻译

    原题戳这里
    自古T1是水题
    因为每一个数字都小于1000,所以对于是否在队列中可以开数组查询
    对于大小的限制,弄一个队列维护大小即可(水题呀。。。)
    这题在Windows下写的,不要在意缩进

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<algorithm>
    using namespace std;
    int ans=0,m,n,a,len=0;
    queue<int> Q;
    int vis[1001];
    int main()
    {
            cin>>m>>n;
            for(int i=1;i<=n;++i)
            {
                 cin>>a;
                 if(!vis[a])
                 {
                         if(len<m)
                         {
                                vis[a]=true;
                                ++ans;
                                Q.push(a);
                                ++len;
                         }
                         else
                         {
                                vis[a]=true;
                                ++ans;
                                vis[Q.front()]=false;
                                Q.pop();
                                Q.push(a);
                         }
                 }
            }
            cout<<ans<<endl;
            return 0;
    }
    

    T2乌龟棋

    题面戳我
    很容易想到DP
    很容易可以想到设f[i][j][k][l][m]表示当前走到第i格,对于每张牌分别用了j,k,l,m张
    但是这样子很显然会炸空间
    继续考虑,如果已经知道每张卡用了多少张,实际上就可以直接计算出当前走到了哪一格,因此第一维可以省去,然后就是一个比较容易的四维DP了

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        register int x=0,t=1;
        register char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-'){t=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
        return x*t;
    }
    //f[x][y][z][w]表示
    //1步的卡片用x张,2步的卡片用y张
    //3步的卡片用z张,4步的卡片用w张
    //的时候,能够拿到最大的分数 
    int f[41][41][41][41];
    int n,m;
    int A[500],B[5];
    int main()
    {
            n=read();
            m=read();
            for(int i=1;i<=n;++i)
              A[i]=read();
            for(int i=1;i<=m;++i)
              B[read()]++;
            f[0][0][0][0]=A[1];//赋初值 
            for(int i=0;i<=B[1];++i)
              for(int j=0;j<=B[2];++j)
                for(int k=0;k<=B[3];++k)
                  for(int l=0;l<=B[4];++l)
                  {
                             int d=A[i+j+j+k+k+k+l+l+l+l+1];
                             if(i!=0)
                                f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l]+d);
                             if(j!=0)
                                f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l]+d);
                             if(k!=0)
                                f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k-1][l]+d);
                             if(l!=0)
                                f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k][l-1]+d);
                  }
            cout<<f[B[1]][B[2]][B[3]][B[4]]<<endl;
            return 0;         
    }
    

    T3关押罪犯

    题目在这里
    这题有难度了,当时我就搞了好久才弄懂。。。。
    如果只说,这道题恐怕也很难说清楚,那么我就尽可能的说清楚。

    首先考虑的的是影响力最小,
    那么我们就贪心的尽可能解决影响力最大的事件
    既然要将人分成两组,那么所有人其实也可以归类为两个集合
    一组是和某个人在一个监狱的集合,
    另一组是和某个人不在某一个监狱的集合。
    现在要考虑的就是解决集合,那么使用并查集
    每次解决一组冲突的方法就是将两个人放到不同的监狱,
    即将某个人放到另一个人不在一个监狱的集合里面。
    如果当前这两个人已经在一个集合中了,那么当前的影响力就是答案,直接输出即可。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define MAX 20010
    #define MAXL 100010
    using namespace std;
    struct Line
    {
          int x,y,w;
    }e[MAXL];
    int n,m,f[MAX];
    int be[MAX];
    inline int getf(int x)
    {
          return x==f[x]?x:f[x]=getf(f[x]);
    }
    bool operator <(Line a,Line b)
    {
           return a.w>b.w;
    }
    int main()
    {
          cin>>n>>m;
          for(int i=1;i<=n*2;++i)
            f[i]=i;
          for(int i=1;i<=m;++i)
                 cin>>e[i].x>>e[i].y>>e[i].w;
          sort(&e[1],&e[m+1]);
          for(int i=1;i<=m;++i)
          {
                  int u=getf(e[i].x);
                  int v=getf(e[i].y);
                  if(u==v)//如果在同一个监狱里 
                  {
                        cout<<e[i].w<<endl;//不可避免的是最大值 
                        return 0;
                  }
                  else
                  {
                      f[v]=getf(e[i].x+n);
                      f[u]=getf(e[i].y+n);
                  }
          }
          cout<<0<<endl;
          return 0;
    }
    
    

    T4引水入城

    题面。。。

    这道题目拆开讲觉得好简单呀,但是组合在一起觉得有些麻烦了。。。。

    首先我们要证明一下如果数据有解的话,每一个地方建造蓄水场,那么它可以影响的旱区的城市一定是一段连续的区间。

    既然有解,证明每一个旱区的城市都可以被影响到,
    利用反证法,假设存在一个地方供水区间分为两段,中间有一个城市不能够被当前位置影响到。
    还是画图吧。。。
    也就是这样子
    这里写图片描述

    但是中间这个玩意能够被其他的地方覆盖到,也就是这样。
    这里写图片描述

    那么这个可以覆盖到的那个蓄水站必然能够影响到左侧的位置
    但是它要影响左侧的位置,它的水的覆盖就穿过了原来的那个供水站的影响位置
    那么证明当前的供水站其实也是可以覆盖到中间的那个位置的。

    如果我还没有讲清楚请画图吧。。。

    好了,
    现在简单了,对于第一问,直接DFS或者BFS直接覆盖一下位置,检查能否覆盖到所有的干旱地区。
    如果可以覆盖的话,上方每一个蓄水站的覆盖区域就是一个线段,
    那么可以贪心的求最少的覆盖数。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int MAX=501;
    int d[4][2]={1,0,-1,0,0,-1,0,1};
    bool map[MAX][MAX];
    bool book[MAX];
    int m,n,g[MAX][MAX];
    int L[MAX],R[MAX];
    int f[MAX];
    struct dsl
    {
        int L;
        int R;
    }syc[MAX];
    bool cmp(dsl a,dsl b)
    {
        if(a.L<b.L)return true;
        else return false;
    }
    void DFS(int x,int y)
    {
        int x1,y1;
        for(int i=0;i<4;++i)
        {
            x1=x+d[i][0];
            y1=y+d[i][1];
            if(x1>n||x1<=0||y1>m||y1<=0||g[x1][y1]>=g[x][y])continue;
            if(map[x1][y1])continue;
             map[x1][y1]=true;
            DFS(x1,y1);
        }
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;++i)
         for(int j=1;j<=m;++j)
             cin>>g[i][j];
        int l,r;
        for(int i=1;i<=m;++i)
        {
            l=MAX;r=0;
            memset(map,0,sizeof(map));
            map[1][i]=true;
            DFS(1,i);
            for(int j=1;j<=m;++j)
             if(map[n][j])
             {
                book[j]=true;
                if(!map[n][j-1]&&map[n][j])l=j;
                if(!map[n][j+1]&&map[n][j])r=j;
             }
            syc[i].L=l;
            syc[i].R=r;
        }
        int count=0;
        bool fl=true;
        for(int i=1;i<=m;++i)
        {
            if(!book[i])
              fl=false,++count;
        }
        if(!fl)
        {
            cout<<0<<endl<<count<<endl;
            return 0;
        }
        else
        {
            memset(f,127,sizeof(f));
            f[0]=0;
            sort(&syc[1],&syc[m+1],cmp);
            for(int i=1;i<=m;++i)
             for(int j=syc[i].L;j<=syc[i].R;++j)
             {
                 f[j]=min(f[syc[i].L-1]+1,f[j]);
             }
            cout<<1<<endl;
            cout<<f[m]<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    BZOJ3585&3339mex——主席树
    BZOJ1926[Sdoi2010]粟粟的书架——二分答案+主席树
    BZOJ2662[BeiJing wc2012]冻结——分层图最短路
    BZOJ1433[ZJOI2009]假期的宿舍——二分图最大匹配
    BZOJ1087[SCOI2005]互不侵犯——状压DP
    BZOJ4808马——二分图最大独立集
    BZOJ3175[Tjoi2013]攻击装置——二分图最大独立集
    BZOJ3524[Poi2014]Couriers——主席树
    BZOJ4010[HNOI2015]菜肴制作——拓扑排序+堆
    BZOJ2588Count on a tree——LCA+主席树
  • 原文地址:https://www.cnblogs.com/cjyyb/p/7481655.html
Copyright © 2011-2022 走看看