1.中位数

【问题描述】

给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。

【文件输入】

第一行为两个正整数n和b,第二行为1~n的排列。

【文件输出】

输出一个整数,即中位数为b的连续子序列个数。

【样例输入1】

5 4

1 2 3 4 5

【样例输出1】

2

【样例输入2】

6 3

1 2 3 4 5 6 3

【样例输出2】

1

 

代码
1 program median;
2  var n,m,i,j,k,st,max,ans:longint;
3 a,s:array[0..100000]of longint;
4 b1,b2:array[-100000..100000,0..1]of longint;
5 begin
6 assign(input,'median.in');reset(input);
7 assign(output,'median.out');rewrite(output);
8 readln(n,m);
9 for i:=1 to n do
10 begin
11 read(a[i]);
12 if a[i]=m then st:=i;
13 end;
14 readln;
15 for i:=st-1 downto 1 do
16 begin
17 if a[i]<a[st] then begin s[i]:=s[i+1]-1;inc(b1[s[i],(st-i) mod 2]);end
18 else
19 begin
20 s[i]:=s[i+1]+1;
21 inc(b1[s[i],(st-i)mod 2]);
22 end;
23 if abs(s[i])>max then max:=abs(s[i]);
24 end;
25 for i:=st+1 to n do
26 begin
27 if a[i]<a[st] then begin s[i]:=s[i-1]-1;inc(b2[s[i],(i-st)mod 2]);end
28 else begin s[i]:=s[i-1]+1;inc(b2[s[i],(i-st)mod 2]);end;
29 if abs(s[i])>max then max:=abs(s[i]);
30 end;
31 ans:=0;
32 for i:=1 to max do
33 ans:=ans+b1[i,0]*b2[-i,0]+b1[-i,1]*b2[i,1];
34 inc(ans,b1[0,0]);
35 inc(ans,b2[0,0]);
36 inc(ans);
37 writeln(ans);
38 close(input);close(output);
39 end.
40

2.打砖块

【问题描述】

在一个凹槽中放置n层砖块,最上面的一层有n块砖,从上到下每层依次减少一块砖,每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示。14154323333376221322312311如果你要敲掉第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。

dp:f[i][j][k]=max{f[i-1][j-k][p]+sum[i][k]}i表示第i列,j表示共打了j个砖块,k表示第i列打了k个砖块

      sum[i][k]表示第i列打前k个砖块的和。

#include<fstream>
using namespace std;
ifstream cin("brike.in");
ofstream cout("brike.out");
int n,m,ans=0;
int f[55][550][55];
int sum[55][55],a[55][55];

int main()
{
    int i,j,k,l;
    cin>>n>>m;
    for (i=1;i<=n;i++)
    for (j=i;j<=n;j++)
      cin>>a[i][j];
    for (j=1;j<=n;j++)
    for (i=1;i<=j;i++)
      sum[j][i]=sum[j][i-1]+a[i][j];
    memset(f,255,sizeof(f));
    for (j=0;j<=n;j++) f[j][0][0]=0;
    for (j=1;j<=n;j++) f[j][1][1]=a[1][j];
    for (i=1;i<=n;i++)
      for (k=0;k<=i;k++)
        for (j=0;j<=m;j++)
          if (k<=j)
            for (l=k-1;l<i;l++)
              if (f[i-1][j-k][l]!=-1)
              {
                     f[i][j][k]>?=f[i-1][j-k][l]+sum[i][k];            
                     if (j==m)
                       ans>?=f[i][j][k];
              }
    cout<<ans<<endl;
    return 0;
}

3.序列合并

【问题描述】

有两个长度为N的序列A和B,在A和B中各任取一个数橡胶可以得到N2个和,求这N2个和中最小的N个。

【文件输入】

第一行输入一个正整数N;第二行N个整数Ai 且Ai≤109;第三行N个整数Bi,且Bi≤109。

【文件输出】

输出仅一行,包含n个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。

【样例输入】

5

1 3 2 4 5

6 3 4 1 7

【样例输出】

2 3 4 4 5

【数据规模】对于50%的数据,满足1≤N≤1000;对于100%的数据,满足1≤N≤100000。

维护一个小根堆。

#include<fstream>
#include<cstdlib>
#define maxn 100010
using namespace std;
ifstream cin("sequence.in");
ofstream cout("sequence.out");
int n;
int a[maxn],b[maxn],c[maxn],a0[maxn],b0[maxn];
int cmp(const void *a,const void *b)
{
    return *(int *)a-*(int *)b;
}
void hsort(int l,int r)
{
     int i=l,j;
     while ((i<<1)<r)
     {
           j=i*2;
           if (j<r&&c[j]>c[j+1])j++;
           if (c[i]>c[j]) {swap(c[i],c[j]);swap(a[i],a[j]);swap(b[i],b[j]);i=j;}else break;
     }
}
int main()
{
    int i,j;
    cin>>n;
    for (i=1;i<=n;i++) cin>>a0[i];
    for (i=1;i<=n;i++) cin>>b0[i];
    qsort(a0+1,n,sizeof(int),cmp);
    qsort(b0+1,n,sizeof(int),cmp);
    for (i=1;i<=n;i++)
    {
        c[i]=a0[i]+b0[1];
        a[i]=i;
        b[i]=1;
    }
    for (i=n/2;i>0;i--) hsort(i,n);
    for (i=1;i<=n;i++)
    {
        if (i>1) cout<<" ";
        cout<<c[1];
        c[1]=a0[a[1]]+b0[++b[1]];
        hsort(1,n);
    }
    return 0;
}

4.最小密度路径

【问题描述】

给出了一张有N个点M条边的加权有向无环图,接下来有Q个询问,每个询问包括2个结点X和Y,要求算出从X到Y的一条路径,使得密度最小(密度的定义为,路径上边的权值和除以边的数量)

【文件输入】

第一行包括2个整数N和M。第2到第M+1行,每行三个数字A、B、W,表示从A到B有一条权值为W的有向边。第M+2行只有一个整数Q。接下来的Q行,每行有两个整数X和Y,表示一个询问。

【文件输出】

对于每个询问输出一行,表示该询问的最小密度路径的密度(保留3位小数),如果不存在从X到Y的一条路径,则输出“OMG!”

【样例输入】

3 3

1 3 5

2 1 6

2 3 6

2

1 3

2 3

【样例输出】

5.000

5.500

【数据范围】对于60%的数据,有1≤N≤10;1≤M≤100,1≤W≤1000,1≤Q≤1000;对于100%的数据,有1≤N≤50;1≤M≤1000,1≤W≤100000,1≤Q≤100000。

floyd 开三维另一维表示两点之间有l条边

#include<cstdio>
#include<cstring>
int n,m,q;
int a[55][55][55]={0};

int main()
{
    freopen("path.in","r",stdin);
    freopen("path.out","w",stdout);
    int i,j,k,l;
    scanf("%d%d\n",&n,&m);
    int x,y,z;
    memset(a,255,sizeof(a));
    for (i=1;i<=m;i++) 
    {
        scanf("%d%d%d\n",&x,&y,&z);
        if (a[x][y][1]==-1||z<a[x][y][1]) a[x][y][1]=z;
    }   
    for (i=1;i<=n;i++) a[i][i][0]=0;
    for (l=2;l<=n;l++)
      for (k=1;k<=n;k++)
        for (i=1;i<=n;i++)
          for (j=1;j<=n;j++)
          { 
                            if (a[i][k][l-1]>-1&&a[k][j][1]>-1&&(a[i][j][l]==-1||a[i][j][l]>a[i][k][l-1]+a[k][j][1]))
                              a[i][j][l]=a[i][k][l-1]+a[k][j][1];                                    
          }
    scanf("%d\n",&q);
    for (i=1;i<=q;i++)
    {
        scanf("%d%d\n",&x,&y);
        double max=111111111;
        for (j=0;j<=n;j++)
          if (a[x][y][j]!=-1&&a[x][y][j]<max*j) max=(double)a[x][y][j]/j;
        if (max!=111111111) printf("%.3f\n",max);else printf("OMG!\n");
    }
    return 0;
}