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数据是不会这么水的。

原文地址:https://www.cnblogs.com/hz-Rockstar/p/11342146.html