• NOIP模拟测试18(T3待更新)


    T1:

      直接模拟,详见代码注释。

      复杂度$O(NM)$。

    Code:

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<queue>
    using namespace std;
    const int N=1010;
    const int M=10010;
    int n,m,tot=0;
    int a[N][N];
    int u[M],d[M],l[M],r[M],bl[M];//上下左右边界及水箱编号
    vector<int> v[M],bx[N],by[N];//v做临接表,bx和by存储水箱边界
    int get()
    {
        char c=getchar();
        while(c!='.'&&c!='-'&&c!='|'&&c!='+'&&(c<'0'||c>'9')) 
            c=getchar();
        if(c>='0'&&c<='9') return c-'0';//水箱编号
        else if(c=='-') return -1;//横向管道
        else if(c=='|') return -2;//纵向管道
        else if(c=='+') return -3;//管道转折,其实和前两个一样,可以不做区分
        else return -4;//空格子
    }
    void find(int id,int x,int y)//二分查找水箱边界
    {
        int xx,yy;
        yy=lower_bound(bx[x].begin(),bx[x].end(),y)-bx[x].begin();
        r[id]=bx[x][yy];l[id]=bx[x][yy-1];
        xx=lower_bound(by[y].begin(),by[y].end(),x)-by[y].begin();
        d[id]=by[y][xx];u[id]=by[y][xx-1];
    }
    void clean(int id)//将整个水箱的区域都标上该水箱的编号
    {
        for(int i=u[id];i<=d[id];i++){
            for(int j=l[id];j<=r[id];j++)
                a[i][j]=id;
        }
    }
    int walk(int x,int y)//沿管道寻找
    {
        a[x][y]=-4;
        if(a[x+1][y]>0) return a[x+1][y];//水箱成树形
        if(a[x+1][y]<=-1&&a[x+1][y]>=-3) return walk(x+1,y);
        if(a[x][y+1]<=-1&&a[x][y+1]>=-3) return walk(x,y+1);
        if(a[x][y-1]<=-1&&a[x][y-1]>=-3) return walk(x,y-1);
    }
    void work(int id)
    {
        for(int i=d[id];i>=u[id];i--){//水必定先进入靠下的水箱,所以从下到上枚举
            if(a[i][l[id]-1]<=-1&&a[i][l[id]-1]>=-3) 
                v[id].push_back(walk(i,l[id]-1));
            if(a[i][r[id]+1]<=-1&&a[i][r[id]+1]>=-3) 
                v[id].push_back(walk(i,r[id]+1));
        }
    }
    void print(int id)//按水箱高度递归输出
    {
        for(int i=0;i<v[id].size();i++)
            print(v[id][i]);
        printf("%d
    ",id);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                a[i][j]=get();//获取格子类型
                if(a[i][j]>=0&&a[i][j-1]>=0){//注意水箱编号大于一位的情况
                    a[i][j]+=10*a[i][j-1];//用类似快读的思想
                    a[i][j-1]=-4;//每个水箱内只能有一个数字
                }
                if(a[i][j]>=-3&&a[i][j]<=-1){//将水箱边界存入
                    bx[i].push_back(j);
                    by[j].push_back(i);
                }
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(a[i][j]>=0){
                    bl[++tot]=a[i][j];//记录水箱编号,水箱编号可能不连续
                    find(a[i][j],i,j);//寻找水箱边界
                }
            }
        }
        for(int i=1;i<=tot;i++) clean(bl[i]);
        for(int i=1;i<=tot;i++) work(bl[i]);
        print(1);
        return 0;
    }
    T1

    T2:

      DP好题,不过暴力也能A。

      题目大意:一条线上N个点,共有精灵M个,时间为一时位于K,精灵都有价值,但一段时间后会消失,求收获的最大权值。

      DP(正解):

      

      搜索(暴力):

      

      暴力压正解,我就不说什么了…

      下面说DP:搜索可以自己想

        设DP数组f[i][j][k],表示i~j已经走过,目前位于i的最大值。

        由于抓取精灵不需要时间,而且权值均为正,所以每次遇到精灵一定会抓。

        先对精灵按照坐标排序。

        将精灵的坐标连同起始点离散,初始化为负无穷,时间为一时起点处权值赋为0。

        这是一个区间DP,先枚举时间,然后枚举区间长度,再枚举左端点,算出右端点。

        然后我们就可以开心地DP了:

          i为时间,L为左端点,R为右端点,val代表精灵的权值,p代表精灵的位置,t为精灵消失的时间。

          f[L-1][R][i+1]=max(f[L-1][R][i+1],f[L][R][i]+val[L-1])  (i+p[L]-p[L-1]<=t[L-1])

          f[L-1][R][i+1]=max(f[L-1][R][i+1],f[L][R][i])      (i+p[L]-p[L-1]>t[L-1])

          f[R+1][L][i+1]=max(f[R+1][L][i+1],f[R][L][i]+val[R+1])  (i+p[R+1]-p[R]<=t[R+1])

          f[R+1][L][i+1]=max(f[R+1][L][i+1],f[R][L][i])      (i+p[R+1]-p[R]>t[R+1])

          f[R+1][L][i+1]=max(f[R+1][L][i+1],f[L][R][i]+val[R+1])  (i+p[R+1]-p[L]<=t[R+1])

          f[R+1][L][i+1]=max(f[R+1][L][i+1],f[L][R][i])      (i+p[R+1]-p[L]>t[R+1])

          f[L-1][R][i+1]=max(f[L-1][R][i+1],f[R][L][i]+val[L-1])  (i+p[R]-p[L-1]<=t[L-1])

          f[L-1][R][i+1]=max(f[L-1][R][i+1],f[R][L][i])      (i+p[R]-p[L-1]>t[L-1])

        后四行的转移方程代表从区间的一头走到另一头再扩展区间,不能丢。

        起点算作一只贡献为0的精灵,方便判断。

        注意判断在该时间内精灵是否已消失。

        DP过程中不断对ans取max,最后的max即为答案。

        共有M只精灵,时间的最大值为T。

        时间复杂度$O(M^2T)$

    Code:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=2010;
     6 const int M=110;
     7 const int inf=9999999;
     8 int n,k,m,t=0,ans=0;
     9 int s[M],dp[M][M][N];
    10 struct point {
    11     int a,b,c;
    12 }p[M];
    13 bool comp(const point a1,const point a2)
    14 {
    15     return a1.a<a2.a;
    16 }
    17 int main()
    18 {
    19     scanf("%d%d%d",&n,&k,&m);
    20     for(int i=1;i<=m;i++){
    21         scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
    22         t=max(p[i].c,t);
    23     }
    24     p[++m].a=k;
    25     sort(p+1,p+m+1,comp);
    26     for(int i=1;i<=t;i++){
    27         for(int j=1;j<=m;j++){
    28             for(int l=1;l<=m-j+1;l++){
    29                 int r=l+j-1;
    30                 dp[l][r][i]=dp[r][l][i]=-inf;
    31             }
    32         }
    33     }
    34     for(int i=1;i<=m;i++){
    35         s[i]=p[i].a;p[i].a=i;
    36         if(s[i]==k)
    37             dp[i][i][1]=p[i].b;
    38     }
    39     for(int i=1;i<=t;i++){
    40         for(int j=1;j<=m;j++){
    41             for(int l=1;l<=m-j+1;l++){
    42                 int r=l+j-1;
    43                 if(dp[l][r][i]>=0){
    44                     if(l>=2){
    45                         int ti=i+s[l]-s[l-1];
    46                         if(ti<=p[l-1].c)
    47                             dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[l][r][i]+p[l-1].b);
    48                         else
    49                             dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[l][r][i]);
    50                         ans=max(ans,dp[l-1][r][ti]);
    51                     }
    52                     if(r<=m-1){
    53                         int ti=i+s[r+1]-s[l];
    54                         if(ti<=p[r+1].c)
    55                             dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[l][r][i]+p[r+1].b);
    56                         else
    57                             dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[l][r][i]);
    58                         ans=max(ans,dp[r+1][l][ti]);
    59                     }
    60                 }
    61                 if(dp[r][l][i]>=0){
    62                     if(l>=2){
    63                         int ti=i+s[r]-s[l-1];
    64                         if(ti<=p[l-1].c)
    65                             dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[r][l][i]+p[l-1].b);
    66                         else
    67                             dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[r][l][i]);
    68                         ans=max(ans,dp[l-1][r][ti]);
    69                     }
    70                     if(r<=m-1){
    71                         int ti=i+s[r+1]-s[r];
    72                         if(ti<=p[r+1].c)
    73                             dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[r][l][i]+p[r+1].b);
    74                         else
    75                             dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[r][l][i]);
    76                         ans=max(ans,dp[r+1][l][ti]);
    77                     }
    78                 }
    79             }
    80         }
    81     }
    82     printf("%d
    ",ans);
    83     return 0;
    84 }
    T2

    建议搜索AC的人打一遍DP,毕竟正经NOIP数据是不会这么水的。

  • 相关阅读:
    ssh免密码登录与常见问题
    在eclipse中生成实体类
    在cmd中设置字体
    java命名
    c/s架构
    sql身份证号查人数
    bootstraptable表格基本
    手机登录验证
    form表单验证提示语句
    html让背景透明
  • 原文地址:https://www.cnblogs.com/hz-Rockstar/p/11342146.html
Copyright © 2020-2023  润新知