2017年12月励志杯月赛 题解

T1 中位数(median.cpp)

【题目描述】
有一个长度为N的数列{A1,A2,…An},这N和数字恰好是1..N的一个排列。你需要统计有多少个子序列{Ai,Ai+1,…Aj}满足:i<=j且j-i+1为奇数,序列的中位数为B。例如{5,1,3}的中位数为3。
【输入文件】
第一行包含两个正整数N和B;第二行包含N个整数,第i个整数为Ai。
【输出文件】
仅包含一个整数,为满足条件的子序列的个数。
【样例输入】
7 4
5 7 2 4 3 1 6
【样例输出】
4
【数据规模】
对于30%的数据中,满足N<=100;
对于60%的数据中,满足N<=1000;
对于100%的数据中,满足N<=100000,1<=B<=N。

【题解】【规律+递推】
【因为B是序列的中位数,所以B一定存在于序列中,所以,记录B在序列中的位置num,并把相对应的数置为0;把小于B的数置为-1;大于B的数置为1。】
【分别从num向左向右扫描,并分别设一个累加器,将枚举时遇到的0、1、-1都加入累加器里,并分别用L、R数组记录累加器中每个数出现的次数】
【最后求:∑L[i]*R[-i](但因为C++没有负数下标,所以要整体向右移n个下标)】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100010],L[200010],R[200010],n,b,num,ans;
int main()
{
    freopen("median.in","r",stdin);
    freopen("median.out","w",stdout);
    int i,j;
    scanf("%d%d",&n,&b);
    for(i=1;i<=n;++i)
     {
        scanf("%d",&a[i]);
        if(a[i]==b) {num=i; a[i]=0; continue;}
        if(a[i]>b) a[i]=1;
         else a[i]=-1;
     }
    int l=n,r=n;
    for(i=num;i>0;--i) l+=a[i],L[l]++;
    for(i=num;i<=n;++i) r+=a[i],R[r]++;
    int len=2*n;
    for(i=0;i<=len;++i)
      ans+=(L[i]*R[len-i]);
    printf("%d\n",ans);
    return 0;
}

T2 敲砖块

【题目描述】
在一个凹槽中放置了N层砖块,最上面的一层油N块砖,从上到下每层一次减少一块砖。每块砖都有一个分支,敲掉这块砖就能得到相应的分值,如图所示。

如果你想敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它;若i>1,则你必须先敲掉第i-1层的第j和第j+1块砖。
你现在可以敲掉最多M块砖,求得分最多能有多少。
【输入文件】
输入文件的第一行有两个正整数N和M;
接下来的N行,描述这N层砖块上的分值A[i,j],满足0<=A[i,j]<=100。
【输出文件】
仅一行,包含一个整数,为最大的得分。
【样例输入】
4 5
2 2 3 4
8 2 7
2 3
49
【样例输出】
19
【数据规模】
对于20%的数据,满足1<=N<=10,1<=M<=30
对于100%的数据,满足1<=N<=50,1<=M<=500。

首先将砖块全部左对齐,变成一个直角三角形的模样。
可以得出如下结论:
1、每一列必须敲到由上到下的若干砖块。
2、如果某一列敲掉了k个砖块,那么其右边的那一列至少敲掉了k-1个砖块。
设f[i][j][k]表示从右到左已经敲到了第i列,其中第i列敲掉了j个砖块且总共敲掉了k个砖块的最大得分.
那么转移方程为f[i][j][k]=max f[i+1][v][k−j]+a[1][i]+a[2][i]+……+a[j][i] (v>=j−1)。
最终答案为max{ f[i][j][k] }。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,a[510][510],fi,fj,fk,v[510][510],f[60][60][510],ans; 
int main()
{
    freopen("brike.in","r",stdin);
    freopen("brike.out","w",stdout);

    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n-i+1;j++)
        {
            scanf("%d",&a[i][j]);
        }

    for (int i=n;i>=1;i--)
      for (int j=1;j<=n-i+1;j++)
         v[i][j]=v[i][j-1]+a[j][i];

    memset(f,-1,sizeof(f));
    f[n+1][0][m]=0;

    for (int j=n;j>=1;j--)
      for (int i=0;i<=n-j+1;i++)
        for (int k=m-i;k>=0;k--)
        {
            for (int l=i-1;l<=n-j+1;l++)
            if (f[j+1][l][k+i]!=-1)
                f[j][i][k]=max(f[j][i][k],f[j+1][l][k+i]+v[j][i]);
            if (ans<f[j][i][k])
            {
                ans=f[j][i][k];
            }
        }  


    printf("%d",ans); 
}

T3 邮递员送信

【问题描述】
有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间。这个邮递员每次只能带一样东西。求送完这N-1样东西并且最终回到邮局最少需要多少时间。
【样例输入】

5 10
2 3 5
1 5 5
3 5 6
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3
3 5 6
5 4 2

【样例输出】

83

【题解 思路1】
题解:大水题,考试想到了两遍最短路,却没有继续向深层思考。只要将边反过来,那之前是1到2~n个点的最短路,反过来就是2~n个点到1的最短路,两遍dijkstra便可。同时要注意题目中有重边出现,用邻接矩阵注意判重,这也显示出了邻接表的优势,邻接表不需要判重。

#include<cmath>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,maxx=1e9,minn,t,z,i,j,k,x,y;
long long ans;
bool flag[1006];
int c[1006],d[1006];
int f1[1006][1006],f2[1006][1006];
int get()
{
    int x=0,p=1;
    char c;
    c=getchar();
    while (c<'0'||c>'9') {if (c=='-') p=-1;c=getchar();}
    while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    return x*p;
}
int main()
{   
    n=get();m=get();
    memset(f1,127/3,sizeof(f1));
    memset(f2,127/3,sizeof(f2));
    memset(c,127/3,sizeof(c));
    memset(d,127/3,sizeof(d));
    maxx=f1[0][0];
    for (i=1;i<=m;i++)
    {
        x=get();y=get();z=get();
        if (f1[x][y]!=maxx) f1[x][y]=min(f1[x][y],z);
        else f1[x][y]=z;
        if (f2[y][x]!=maxx) f2[y][x]=min(f2[y][x],z);
        else f2[y][x]=z;
    }
    memset(flag,false,sizeof(flag));
    for (i=1;i<=n;i++) c[i]=f1[1][i];
    flag[1]=true;
    c[1]=0;
    for (i=1;i<=n-1;i++)
    {
        minn=maxx;
        k=0;
        for (j=1;j<=n;j++)
            if ((!flag[j])&&(c[j]<minn))
            {
                minn=c[j];
                k=j;
            }
        if (k==0) break;
        flag[k]=true;
        for (j=1;j<=n;j++)
        {
            if (c[k]+f1[k][j]<c[j])
                c[j]=c[k]+f1[k][j];
        }
    }
    memset(flag,false,sizeof(flag));
    for (i=1;i<=n;i++) d[i]=f2[1][i];
    flag[1]=true;
    d[1]=0;
    for (i=1;i<=n-1;i++)
    {
        minn=maxx;
        k=0;
        for (j=1;j<=n;j++)
            if ((!flag[j])&&(d[j]<minn))
            {
                minn=d[j];
                k=j;
            }
        if (k==0) break;
        flag[k]=true;
        for (j=1;j<=n;j++)
        {
            if (d[k]+f2[k][j]<d[j])
                d[j]=d[k]+f2[k][j];
        }
    }
    for (i=1;i<=n;i++)
        ans+=c[i]+d[i];
     printf("%lld",ans);  
}

【题解】【最短路】
【这道题思路十分精妙,因为是有向图,所以刚开始按数据建图,跑一遍SPFA;再把所有边反向,再跑一遍SPFA。把两次的最短路都加入答案】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int f[1010][1010];
int a[100010],nxt[100010],p[1010],val[100010],tot;
int n,m;
ll ans,dis[1010];
inline void add(int x,int y,int v)
{
    tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot; val[tot]=v;
}
inline void spfa()
{
    queue<int>que;
    memset(dis,127/3,sizeof(dis));
    dis[1]=0; que.push(1);
    while(!que.empty())
     {
        int u=que.front(); que.pop();
        int v=p[u];
        while(v!=-1)
         {
            if(dis[a[v]]>dis[u]+(ll)val[v])
             {
                dis[a[v]]=dis[u]+(ll)val[v];
                que.push(a[v]);
               }
            v=nxt[v];
          }
     }
    for(int i=2;i<=n;++i) ans+=dis[i];
    return;
}
int main()
{
    freopen("post.in","r",stdin);
    freopen("post.out","w",stdout);
    int i,j;
    memset(f,127,sizeof(f));
    memset(p,-1,sizeof(p));
    memset(nxt,-1,sizeof(nxt));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;++i)
      {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(f[x][y]>z) f[x][y]=z;
        add(x,y,z);
      }
    spfa(); tot=0;
    memset(p,-1,sizeof(p));
    memset(nxt,-1,sizeof(nxt));
    for(i=1;i<=n;++i)
     for(j=1;j<=n;++j)
      if(i!=j&&f[i][j]!=f[0][0])
       add(j,i,f[i][j]);
    spfa();
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值