zoukankan      html  css  js  c++  java
  • 清北

                            笔记
    【问题描述】
    给定一个长度为m的序列a,下标编号为1~m。序列的每个元素都是1~n的
    整数。定义序列的代价为

    你现在可以选择两个数x和y,并将序列a中所有的x改成y。x可以与y相等。
    请求出序列最小可能的代价。
    【输入格式】
    输入第一行包含两个整数n和m。第二行包含m个空格分隔的整数,代表序
    列a。
    【输出格式】
    输出一行,包含一个整数,代表序列最小的代价。
    【样例输入 1】
    4 6
    1 2 3 4 3 2
    【样例输出 1】
    3
    【样例输入 2】
    10 5
    9 4 3 8 8
    【样例输出 1】
    6
    【样例解释】
    样例 1 中,最优策略为将 4 改成 3。样例 2 中,最优策略为将 9 改成 4。
    【数据规模和约定】

    对于30%的数据,n,m<=100.
    对于60%的数据,n,m ≤ 2000。
    对于100%的数据,1 ≤ n,m≤ 100,000。

    题解:由于题目要求序列的最小代价,因此原代价-max(x与它在序列中左右两边数的差值-替换他的y在序列中左右两边数的差值)即为答案。

            考虑用哪个数替换x:

       当与x相邻的数有奇数个时,取中间的数为y。因为改变中间的数对两边的贡献一样。比如样例1:与2相邻的为1,3,3,原本与2相邻的代价为1,1,1。将2替换为3时,代价为2,0,0.左边的代价加1,右边的代价-1.相当于改变中间的数对其两边代价无影响,影响的只是中间数的代价,因此取中间数为y,此时中间数代价为0。而若是取其他的数,对其左边或右边代价产生影响。

       当与y相邻的数有偶数个时,取中间两个数之间的任意数为y均可。比如样例1:与3相邻的为2,2,4,4。取2<=y<=4均可。原本与3相邻的代价为1,1,1,1。总代价为4.将3替换为2时,代价为0,0,2,2.总代价为4.将3替换为4时,代价为2,2,0,0,总代价也为4.

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #define ll long long
    #define N 100010
    using namespace std;
    ll n,m,ans=0x7fffffff/3,sum(0);
    ll a[N],b[N],c[N];
    ll f[N][3],ff[N];
    bool fi[N]={0};
    void erfen(int k,int l,int r)
    {
        if (l>r) return ;
        int mid=(l+r)>>1;
        a[0]=mid;
        int ki=b[k],sum1(0),sum2(0);
        if (mid-a[b[k]-1]>0) sum1+=mid-a[b[k]-1];
          else sum2+=mid-a[b[k]-1];
        if (a[b[k]+1]-mid>0) sum2+=mid-a[b[k]+1];
          else sum1+=mid-a[b[k]+1];
        while (ff[ki])
          {
               if (mid-a[ff[ki]-1]>0) sum1+=mid-a[ff[ki]-1];
                 else sum2+=mid-a[ff[ki]-1];
               if (a[ff[ki]+1]-mid>0) sum2+=mid-a[ff[ki]+1];
               else sum1+=mid-a[ff[ki]+1];
               ki=ff[ki];
          }
        ans=min(ans,sum-f[k][0]+f[k][1]-f[k][2]+sum1-sum2);
        if (-sum2>sum1) erfen(k,mid+1,r);
        else erfen(k,l,mid-1);
        
    }
    int main()
    {
        freopen("note.in","r",stdin);
        freopen("note.out","w",stdout);
        
        scanf("%I64d%I64d",&n,&m);
        for (int i=1;i<=m;i++) 
          {
               scanf("%I64d",&a[i]);
               fi[a[i]]=1;
               a[0]=a[1];
               if (a[i]-a[i-1]>0)
                 {
                        f[a[i]][0]=a[i]-a[i-1],sum=sum+a[i]-a[i-1];    
                   f[a[i-1]][2]=a[i]-a[i-1]; 
               } 
             else 
               {
                     f[a[i]][1]=a[i]-a[i-1],sum=sum+a[i-1]-a[i];
                     f[a[i-1]][2]=a[i-1]-a[i];
               }
              
          }
        for (int i=m;i>=1;i--)
          {
               if (!b[a[i]]) b[a[i]]=i;
               else  ff[i]=b[a[i]],b[a[i]]=i;
          }
        for (int i=1;i<=n;i++)
          if (fi[i]) erfen(i,0,n);
        printf("%I64d
    ",ans);
          
        fclose(stdin);
        fclose(stdout);
        
        return 0;
        
    }
    我可怜的25分代码

    考试的时候从0~n枚举将x变为几。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<ios>
    #include<vector>
    #define ll long long
    using namespace std;
    const int N = (int) 1e5;
    vector<int> v[N+10];
    int a[N+10];
    int n,m;
    ll ans(0),sum(0);
    int main()
    {
        freopen("note.in","r",stdin);
        freopen("note.out","w",stdout);
        
           scanf("%d%d",&n,&m);
           for (int i=1;i<=m;i++) scanf("%d",&a[i]);    
           for (int i=1;i<=m;i++)
             {
                if (i!=1&&a[i]!=a[i-1]) v[a[i]].push_back(a[i-1]);//将与a[i]相邻的不同的数储存进数组 
             if (i!=m&&a[i]!=a[i+1]) v[a[i]].push_back(a[i+1]);     
          }
        for (int i=1;i<=n;i++)
          {
               int k=v[i].size();
               if (k)
                 {
                       sort(v[i].begin(),v[i].end());
                      ll ki=v[i][k>>1];      //当k为奇数时,取中间的数,其对两边数的贡献是一样的。 
                                             //当k为偶数时,取中间两个数之间的任意数都可,对其两边数的贡献一样。 
                       ll sum1(0),sum2(0);
                       for (int j=0;j<k;j++)
                         {
                                sum1+=(ll)abs(i-v[i][j]);
                                sum2+=(ll)abs(ki-v[i][j]);
                    }
                 ans=max(ans,sum1-sum2);
                 sum+=sum1;
               } 
          }
        cout<<sum/2-ans<<endl;//由于a[i+1]-a[i]在计算a[i]时计算了一次。
                             //在a[i+1]时又计算了一次,因此除以2 
        fclose(stdin);
        fclose(stdout);
        return 0;
        
    } 
    100分

                         括号
    【问题描述】
    有一个长度为n的括号序列,以及k种不同的括号。序列的每个位置上是哪
    种括号是随机的,并且已知每个位置上出现每种左右括号的概率。求整个序列是
    一个合法的括号序列的概率。
    我们如下定义合法括号序列:
     空序列是合法括号序列;
     如果A是合法括号序列,那么lAr是合法括号序列,当且仅当l和r是同种
    的左右括号;
     如果A和B是合法括号序列,那么AB是合法括号序列。
    【输入格式】
    输入第一行包含两个整数n和k。接下来的输入分为n组,每组k行。第i组第
    j行包含两个实数l[i,j]和r[i,j],分别代表第i个位置上是第j类的左括号和右括号
    的概率。
    【输出格式】
    输出一行,包含一个实数,代表序列是合法括号序列的概率。建议保留至少
    5 位小数输出。只有当你的输出与标准答案之间的绝对误差不超过10 −5 时,才会
    被判为正确。
    【样例输入 1】
    2 1
    1.00000 0.00000
    0.00000 1.00000
    【样例输出 1】
    1.00000
    【样例输入 2】
    4 1
    0.50000 0.50000
    1.00000 0.00000
    0.00000 1.00000
    0.50000 0.50000
    【样例输出 2】
    0.25000
    【数据规模和约定】
    对于20%的数据,n ≤ 50,k = 1,所有位置的概率非 0 即 1。
    另外有 30%的数据,n ≤ 34,k = 1,前 10 个和后 10 个位置的所有概率都
    是 0.5,中间剩余位置的概率非 0 即 1。
    80%的数据,n,k ≤ 50。
    对于100%的数据,1 ≤ n ≤ 200,1 ≤ k ≤ 50。

    考场:考试的时候只想到了50%的数据怎么做,画蛇添足加上了关于输出结果是0的判断,只得了30分,然而删掉后,可以得50分。好伤心。

    题解:空序列直接判断n是否等于0.用f数组储存第二种括号序列概率,用g数组储存第三种括号序列概率。

            枚举序列的长度l,f[i][i+l-1]+=(f[i+1][i+l-2]+g[i+1][i+l-2])*L[i][j]*R[i+l-1][j];//在合法序列两边加上一对括号,概率就是中间是合法括号序列的概率*序列开头是左括号的概率*序列结尾是右括号的概率。

            g[i][i+l-1]+=f[i][j]*(f[j+1][i+l-1]+g[j+1][i+l-1]);//前一部分括号序列为f的概率和后一部分是合法括号序列的概率。//为什么前一部分只有f的概率?例如:()()()这个好像可以从位置5Chu前一部分分成g序列,后一部分f序列,但实际当你在求位置2的时候,这种情况已经计算了。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #define ll long long
    #define N 210
    using namespace std;
    int n,k;
    double a[N][N][3],f[N][N][N]={0},ans(0);
    int num[N][2]={0};
    int main()
    {
        freopen("brackets.in","r",stdin);
        freopen("brackets.out","w",stdout);
        
        scanf("%d%d",&n,&k);
        for (int i=1;i<=n;i++)
          for (int j=1;j<=k;j++)
              {
                double x,y;
                scanf("%lf%lf",&x,&y);
                a[i][j][1]=x;
                a[i][j][2]=y;
              }
        for (int i=1;i<=k;i++) f[1][i][1]=a[1][i][1];
        for (int i=2;i<=n;i++)
          {
              for (int ki=1;ki<=k;ki++)
                for (int j=0;j<i;j++)
                  {
                     f[i][ki][j+1]+=f[i-1][ki][j]*a[i][ki][1];
                     if (j-1>=0) f[i][ki][j-1]+=f[i-1][ki][j]*a[i][ki][2];    
                }
          }
        for (int i=1;i<=k;i++) ans+=f[n][i][0];
        printf("%.5lf
    ",ans);
        fclose(stdin);
        fclose(stdout);
        
        return 0;
        
    }
    50分
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define N 210
    #define M 60
    using namespace std;
    int n,k;
    double L[N][M],R[N][M],f[N][N]={0},g[N][N]={0};
    int main()
    {
        freopen("brackets.in","r",stdin);
        freopen("brackets.out","w",stdout);
        scanf("%d%d",&n,&k);
        if (n==0) printf("%0.5lf
    ",1);//空序列 
        else 
          {
              for (int i=1;i<=n;i++)
              for (int j=1;j<=k;j++)
                scanf("%lf%lf",&L[i][j],&R[i][j]);
            for (int i=1;i<=n;i++) f[i+1][i]=1;
              for (int l=2;l<=n;l+=2)
                for (int i=1;i<=n-l+1;i++)
                  {
                     for (int j=1;j<=k;j++) f[i][i+l-1]+=(f[i+1][i+l-2]+g[i+1][i+l-2])*L[i][j]*R[i+l-1][j];
                     for (int j=i+1;j<i+l-1;j+=2) g[i][i+l-1]+=f[i][j]*(f[j+1][i+l-1]+g[j+1][i+l-1]);
                  }
             double ans=f[1][n]+g[1][n];
             printf("%.5lf
    ",ans);
          }
        fclose(stdin);
        fclose(stdout);
        return 0;
    } 
    dp(100分)

                        城堡
    【问题描述】
    给定一张N个点M条边的无向连通图,每条边有边权。我们需要从M条边中
    选出N − 1条, 构成一棵树。 记原图中从 1 号点到每个节点的最短路径长度为Di ,
    树中从 1 号点到每个节点的最短路径长度为? ? ,构出的树应当满足对于任意节点
    i,都有Di = Si 。
    请你求出选出N − 1条边的方案数。
    【输入格式】
    输入的第一行包含两个整数N和M。
    接下来M行,每行包含三个整数u、v和w,描述一条连接节点u和v且边权为
    w的边。
    【输出格式】
    输出一行,包含一个整数,代表方案数对2^31 − 1取模得到的结果。
    【样例输入】
    3 3
    1 2 2
    1 3 1
    2 3 1
    【样例输出】
    2
    【数据规模和约定】
    对于20%的数据,2<=N<=5,M<=10.
    对于50%的数据,满足条件的方案数不超过 10000。
    对于100%的数据,2≤ N ≤ 1000,N-1  ≤ M ≤ N(N-1)/2,1<=w<=100.

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define N 1100
    #define NN 1000010
    #define ll long long
    using namespace std;
    int n,m,num(0),Head,Tail;
    int head[N],team[NN];
    ll sum[N]={0},ans(1);
    ll dis[N];
    bool f[N];
    const ll M=1<<31-1;
    struct node
    {
        int v,t,pre;
    }e[NN];
    void xx()
    {
        Head=0;Tail=1;
        memset(f,0,sizeof(f));
        memset(team,0,sizeof(team));
        team[1]=1;f[1]=1;sum[1]=1;
    }
    void add(int from,int to,int w)
    {
        e[++num].v=to;
        e[num].t=w;
        e[num].pre=head[from];
        head[from]=num;
    }
    void spfa()
    {
        for (int i=1;i<=n;i++) dis[i]=0x7fffffff;
        dis[1]=0;f[1]=1;team[1]=1;Head=0;Tail=1;
        while (Head<=Tail)
          {
               int ki=team[++Head];
               f[ki]=0;
               for (int i=head[ki];i;i=e[i].pre)
                 {
                        int u=e[i].v;
                     if (dis[u]>dis[ki]+e[i].t)
                          {
                                dis[u]=dis[ki]+e[i].t;
                                if (!f[u])
                                  {
                                      f[u]=1;
                                      team[++Tail]=u;
                            }
                      }
               }
          }
    }
    int main()
    {   
        freopen("castle.in","r",stdin);
        freopen("castle.out","w",stdout);
        scanf("%d%d",&n,&m);
        for (int i=1,x,y,z;i<=m;i++)
          {
               scanf("%d%d%d",&x,&y,&z);
               add(x,y,z);
               add(y,x,z);
          }
        spfa();
        xx();
        while (Head<=Tail)
          {
               int ki=team[++Head];
               f[ki]=0;
               for (int i=head[ki];i;i=e[i].pre)
                 {
                        int u=e[i].v;
                     if (dis[u]==dis[ki]+e[i].t) 
                      {
                           sum[u]++;
                           if (sum[u]>=M) sum[u]-=M;
                           if (!f[u]) team[++Tail]=u,f[u]=1;
                      }
               }
          }
        for (int i=1;i<=n;i++) ans=(ans*sum[i])%M;
        cout<<ans<<endl;    
        fclose(stdin);
        fclose(stdout);
        return 0;
    } 
    需更正
  • 相关阅读:
    李宏毅机器学习课程笔记-9.3RNN的应用
    李宏毅机器学习课程笔记-9.2如何训练RNN
    李宏毅机器学习课程笔记-9.1循环神经网络RNN入门
    李宏毅机器学习课程笔记-8.2图神经网络(Spatial-based Convolution)
    李宏毅机器学习课程笔记-7.4基于CNN和PyTorch的食物图片分类
    PyTorch入门:基于LeNet5和CIFAR10的图片分类
    PyTorch入门:使用PyTorch搭建神经网络LeNet5
    李宏毅机器学习课程笔记-8.1图神经网络入门
    李宏毅机器学习课程笔记-7.3CNN应用案例
    李宏毅机器学习课程笔记-7.2CNN学到了什么
  • 原文地址:https://www.cnblogs.com/sjymj/p/6059287.html
Copyright © 2011-2022 走看看