• 道路 [NOIP模拟]


    Description

    我们看见了一个由 m 行 n 列的 1*1 的格子组成的矩阵,每个格子(I,j)有对应的高度 h[i][j]和初始的一个非负权值 v[i][j].我们可以随便选择一个格子作为起点,然后在接下来的每一步当中,我们能且只能到达与当前格子有边相邻的四个格子中的高度不超过当前格子高度的格子,每当我们到达一个新格子(包括一开始选择的初始格子),我们就能得到该格子的权值分,然后该格子的权值就会等概率变成不比当前的权值大的一个非负权值。每一个格子在满足前面条件情况下,可以走任意多次。我们希望得到一个最大的期望权值和路径,并给出这个最大的期望权值和。

    Input
    第一行两个正整数表示 m,n。
    接下来的 m 行,每行 n 个正整数,表示 h[i][j].
    接下来的 m 行,每行 n 个非负整数,表示 v[i][j].

    Output
    一行一个实数,表示所求的最大期望权值和。保留零位小数。

    Sample Input
    1 3
    1 2 1
    1 2 3

    Sample Output
    5

    Data Range
    对于 30%的数据,保证 n,m 不超过 100.
    对于另外 20%的数据,保证不存在相同的高度的格子。
    对于 100%的数据,保证 n,m 不超过 1000.所有权值与高度不超过 10^9.

    Solution

    对于高度相同的点可以重复来回走,把他们整体看成一个点,权值为v,每走完一次,他的权值变成[0,v]内的一个值的概率一样,那么期望的平均值为0.5v。每走一次,v'=0.5v,那么看成一个等比数列{vi},其中v1=v,vi=0.5vi-1,那么记等差数列前i项和为Si,当i→+∞时,Si→2v(形象理解:一个饼每次切1/2,最后切到无限小;严谨推理:等比数列求和公式,在次不做赘述)

    那么对于相同高度的点,缩成一个点,如果缩点前的点数>1,最后权值×2,最后对于每个缩的点跑一边最长路即可(SPFA/DP)

    要注意缩点BFS的写法,不要队列弹出时再记录,加入队列前就记录,否则可能会WA

    Code

     1 #include<set>
     2 #include<map>
     3 #include<queue>
     4 #include<stack>
     5 #include<cstdio>
     6 #include<cstring>
     7 #include<iostream>
     8 #include<algorithm>
     9 #define inf (1<<30)
    10 #define ll long long
    11 #define RG register int
    12 #define rep(i,a,b)    for(RG i=a;i<=b;i++)
    13 #define per(i,a,b)    for(RG i=a;i>=b;i--)
    14 #define maxn 1005
    15 #define add(x,y) e[++cnt].v=y,e[cnt].next=head[x],head[x]=cnt
    16 using namespace std;
    17 typedef pair<int,int> pir;
    18 int n,m,id,cnt;
    19 int h[maxn][maxn],val[maxn][maxn],head[maxn*maxn];
    20 int rec[maxn][maxn];
    21 int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
    22 ll ans,V[maxn*maxn],f[maxn*maxn];
    23 struct E{
    24     int v,next;
    25 }e[4000005];
    26 inline int read()
    27 {
    28     int x=0,f=1;char c=getchar();
    29     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    30     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    31     return x*f;
    32 }
    33 
    34 void DO(int x,int y)
    35 {
    36     ++id;int num=0;
    37     queue<pir> que;
    38     que.push((pir){x,y});
    39     pir u;int tx,ty;
    40     while(!que.empty())
    41     {
    42         u=que.front();que.pop(),++num;
    43         rec[u.first][u.second]=id,V[id]+=val[u.first][u.second];
    44         rep(i,0,3)
    45         {
    46             tx=u.first+dir[i][0],ty=u.second+dir[i][1];
    47             if(tx<1||tx>n||ty<1||ty>m)    continue;
    48             if(rec[tx][ty])                continue;
    49             if(h[tx][ty]!=h[u.first][u.second])    continue;
    50             que.push((pir){tx,ty});
    51         }
    52     }
    53     if(num>1)    V[id]<<=1;
    54 }
    55 
    56 ll LongPath(int u)
    57 {
    58     if(f[u]!=-1)    return f[u];
    59     f[u]=0;
    60     for(int i=head[u];i;i=e[i].next)
    61     {
    62         f[u]=max(f[u],LongPath(e[i].v));
    63     }
    64     f[u]+=V[u];
    65     return f[u];
    66 }
    67 
    68 int main()
    69 {
    70     freopen("road.in","r",stdin);
    71     freopen("road.out","w",stdout);
    72     n=read(),m=read();
    73     rep(i,1,n)rep(j,1,m)    h[i][j]=read();
    74     rep(i,1,n)rep(j,1,m)    val[i][j]=read();
    75     rep(i,1,n)rep(j,1,m)    if(!rec[i][j])    DO(i,j);
    76     int tx,ty;
    77     rep(x,1,n)rep(y,1,m)rep(i,0,3)
    78     {
    79         tx=x+dir[i][0],ty=y+dir[i][1];
    80         if(tx<1||tx>n||ty<1||ty>m||h[tx][ty]>=h[x][y])    continue;
    81         add(rec[x][y],rec[tx][ty]);
    82     }
    83     memset(f,-1,sizeof(f));
    84     rep(i,1,id) ans=max(ans,LongPath(i));
    85     cout<<ans;
    86     return 0;
    87 }
    View Code
  • 相关阅读:
    Python/WSGI 应用快速入门--转
    汇编题目:数字转字符,并在窗口上显示出来
    汇编题目:在窗口上显示Welcome to masm!
    VBA中的函数Timer用法
    用VBA计算两个日期之间的工作日(去掉周末两天)
    VBA记录当前系统时间并精确到毫秒
    上海房产税免征--积分或居住证
    学习汇编语言
    “Hello World”—— 第一个汇编程序
    汇编程序设计上机步骤
  • 原文地址:https://www.cnblogs.com/ibilllee/p/7798404.html
Copyright © 2020-2023  润新知