• vijos 1282&1283&1284&1285 佳佳的魔法照片/魔法药水/魔杖/魔法阵


    题目链接:

    https://vijos.org/p/1282

    https://vijos.org/p/1283

    https://vijos.org/p/1284

    https://vijos.org/p/1285

    T1:佳佳的魔法照片

     纯模拟,我用了两次快排,但是有个数据中k为0也就是无输出,需要注意。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    struct ding{
      int node,v;
    }a[50010];
    int n,k,e[15];
    inline int read()
    {
      int ef=0; char ch;
      while ((ch>'9')||(ch<'0')) ch=getchar();
      while ((ch>='0')&&(ch<='9'))
      {
          ef=ef*10+ch-'0';
          ch=getchar();
      }
      return ef;
    }
    bool cmp(const ding&x,const ding&y){return (x.v==y.v?x.node<y.node:x.v>y.v);}
    bool check()
    {
      for (int i=1;i<=n-1;i++)
      if (a[i].v<a[i+1].v) return false;
      return true;
    }
    int main()
    {
      n=read(); k=read(); 
      for (int i=1;i<=10;i++) e[i]=read();
      for (int i=1;i<=n;i++) 
      {
        a[i].v=read();
        a[i].node=i;
      }
      if (!check()) sort(a+1,a+1+n,cmp);
      for (int i=1;i<=n;i++) a[i].v+=e[(i-1)%10+1];
      if (!check()) sort(a+1,a+1+n,cmp);
      for (int i=1;i<=k-1;i++) printf("%d ",a[i]);
      if (k!=0) printf("%d
    ",a[k]);
      return 0;
    } 

    T2:佳佳的魔法药水

    后来问了一些同学,发现他们大多数都是取当前价格最小的那个(因为它的价格不可能再被更新了),然后去更新可以由它合成的药水的价格(但是要保证另外一种需要的药水,在此之前也已经被选为用来更新的药水了)。然而弱弱的我并没有想到这种方法,于是乎我就打了个dfs搜索。。。

    当然,完全硬来肯定是不行的,首先如果当前这瓶药水的价格已经得出了,那下次访问的时候我们就返回上次得到的最优解,在存在环的情况时,我们返回的就是这瓶药水的价格和为1的方案数。

    下面是具体程序:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cstdlib>
    using namespace std;
    char ch;
    inline int read()
    {
      int ef=0; 
      while ((ch>'9')||(ch<'0')) ch=getchar();
      while ((ch>='0')&&(ch<='9'))
      {
          ef=ef*10+ch-'0';
          ch=getchar();
      }
      return ef;
    }
    struct ding{
      int a,b;
    };
    int val[2000],f[2000],g[2000],n,vis[2000];
    vector<ding>p[2000];
    ding dfs(int x)
    {
      if (vis[x]) return ((ding){val[x],1}); //如果有环的话
      if (f[x]) return ((ding){f[x],g[x]});//如果已经做过一次
      vis[x]=true;
      int sum=val[x],sum2=0;
      for (int i=0;i<p[x].size();i++)
      {
          ding now=p[x][i];
        ding w1=dfs(now.a),w2=dfs(now.b);
    //更新
        if ((w1.a+w2.a)<sum)
        {
          sum=w1.a+w2.a;
          sum2=w1.b*w2.b;    
        } 
        else if (w1.a+w2.a==sum) sum2+=w1.b*w2.b;
      }
      if (sum==val[x]) sum2++;
      f[x]=sum; g[x]=sum2;
      vis[x]=false;
      return ((ding){f[x],g[x]});
    }
    int main()
    {
      //freopen("hahain.txt","r",stdin);
      //freopen("hahaout.txt","w",stdout);
      n=read();
      for (int i=0;i<=n-1;i++) val[i]=read();
      int a1,b1,c1;
      while (scanf("%d%d%d",&a1,&b1,&c1)!=EOF) p[c1].push_back((ding){a1,b1});
      ding ans=dfs(0);
      printf("%d %d
    ",ans.a,ans.b);
      return 0;
    }

    T3:佳佳的魔杖

    第一次做到这么设的动归,蛮有意义的。

    我们设f[i][j]为最后一段起始点在i之前(包括i),终结点在j之前(包括j),i>=j;

    很显然f[i][j]=max(f[i-1][j],f[i][j-1](j-1>=i));就是我们并没有做出新的魔杖。

    但是如果我们要做出新的魔杖呢?那么f[i][j]=f[i-1][j-1]+segma(m[now](now=i;now<=j))(segma(l[now])要在lo和hi之间)。

    它的意思是什么呢?就是我们用i~j的这几段做一根新的魔杖,首先长度需要在lo和hi之间,其次,为了避免冲突,上一次的魔杖最多可能是i-1~j-1或者在这之前。

    程序:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    using namespace std;
    int n;
    long long l[1010],m[1010],lo,hi,f[1010][1010];
    int main()
    {
      long long x;
      scanf("%d%lld%lld",&n,&lo,&hi);
      for (int i=1;i<=n;i++) 
      {
          scanf("%lld",&x);
          l[i]+=l[i-1]+x;
      }
      for (int i=1;i<=n;i++)
      {
          scanf("%lld",&x);
          m[i]+=m[i-1]+x;
      }
      for (int i=1;i<=n;i++)
       for (int j=1;j<=n;j++)
       {
         f[i][j]=f[i-1][j];
         if (j-1>=i) f[i][j]=max(f[i][j],f[i][j-1]);
         if ((l[j]-l[i-1]>=lo)&&(l[j]-l[i-1]<=hi)) f[i][j]=max(f[i][j],f[i-1][j-1]+m[j]-m[i-1]);
       }
      printf("%lld
    ",f[n][n]);  
      return 0;
    }

    T4:佳佳的魔法阵

    就是直接dfs再加上两个剪枝。

    可行性剪枝,如果一个点的上面和下面都被访问过了但是左右还未被访问(或者左右都已访问,可上下还没有),说明左右两块区间在当前走一步之后,无法联通,直接退出。

    最优性剪枝:当前的最大值已经大于更新过的答案了,那就直接退出。

    程序:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define INF 2100000000
    using namespace std;
    int node,n,m,ans,k1,k2;
    int h[5]={0,-1,1,0,0},l[5]={0,0,0,-1,1};
    pair<int,int>f[3000];
    bool vis[60][60];
    void dfs(int x,int y,int node,int sum)
    {
      int now=node%(n*m/2);
      if (node<=n*m/2) f[now]=make_pair(x,y);
      else 
      {
          pair<int,int>p=f[now];
          sum=max(sum,k1*abs(p.first-x)+k2*abs(p.second-y));
          if (sum>ans) return;
      }
      //cout<<x<<" "<<y<<" "<<node<<" "<<sum<<endl;
      if (node==n*m) {ans=min(ans,sum);return;}
      if ((vis[x][y-1]==vis[x][y+1])&&(vis[x-1][y]==vis[x+1][y]))
      {
          bool f1=vis[x-1][y]&vis[x+1][y];
        bool f2=vis[x][y-1]&vis[x][y+1];
        if (f1!=f2) return;
        if (f1&f2) return;
      }
      for (int i=1;i<=4;i++)
      if (!vis[x+h[i]][y+l[i]])
      {
          vis[x+h[i]][y+l[i]]=true;
        dfs(x+h[i],y+l[i],node+1,sum);
        vis[x+h[i]][y+l[i]]=false;
      }
    }
    int main()
    {
      scanf("%d%d%d%d",&n,&m,&k1,&k2);
      for (int i=0;i<=m+1;i++) vis[0][i]=vis[n+1][i]=true;
      for (int i=0;i<=n+1;i++) vis[i][0]=vis[i][m+1]=true;
      for (int i=0;i<=n*m;i++) f[i]=make_pair(0,0);
      ans=INF;
      vis[1][m]=true;
      dfs(1,m,1,0);
      printf("%d
    ",ans);
      return 0;
    }
  • 相关阅读:
    内置的包装类
    子类继承父类的哪些成员
    JAVA笔记140-使用this语句解决构造器重载相互调用问题
    Java学习
    AngularJs2
    Angular
    检测是否所有的栏位都已经填充了内容了。(可以用来判断动态放置的东西和外加的框是否一致)
    上下各有一个框,框里有元素(点击下面的元素,显示到上面的框里面去,按一定顺序排放)
    Nashorn 在JDK 8中融合Java与JavaScript之力
    2014年Facebook的开源成就
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6677097.html
Copyright © 2020-2023  润新知