20161004模拟

试题+评测数据下载:https://yunpan.cn/cvVyW73vH8tiy (提取码:dd21)

类型分析:

T1 bfs+hash判重

T2 dfs+dp

T3 线段树(参见苹果树)

PA

【题目描述】

汉诺塔升级了:现在我们有个圆盘和个柱子,每个圆盘大小都不一样,大的圆盘不能放在小的圆盘上面,个柱子从左到右排成一排。每次你可以将一个柱子上的最上面的圆盘移动到右边或者左边的柱子上(如果移动之后是合法的话)。现在告诉你初始时的状态,你希望用最少的步数将第大的盘子移动到第根柱子上,问最小步数。

【输入格式】

第一行一个正整数,代表询问的组数。

接下来组数据,每组数据第一行一个整数。

接下来一行每行个正整数,代表每个柱子上圆盘的大小。

【输出格式】

输出共行,代表每次的答案。如果方案不存在,输出“”。

【样例输入】

4

3

2 1 3

2

7 8

2

10000 1000

3

97 96 95

【样例输出】

4

0

-1

20

【样例解释】

无。


【数据范围与规定】
对于70%的数据,N的值都是相等的。

对于100%的数据,1 ≤ T ≤ 6 × 103 ,1 ≤N ≤ 7。

AC代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
inline const int read(){
    register int x=0;
    register char ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x; 
}
const int N=10;
const int M=8e7;
const int w[]={0,1,10,100,1000,10000,100000,1000000};
int n,a[N],b[N],c[N],dis[M];
bool vis[M];
int main(){
    freopen("huakai.in","r",stdin);
    freopen("huakai.out","w",stdout);
    int T,n;T=read();
    while(T--){
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        if(!n){puts("-1");continue;}
        if(n==1){puts("0");continue;}
        if(n==2){a[1]<=a[2]?puts("0"):puts("-1");continue;}
        if(n==3){
            if(a[1]<a[2]&&a[2]<a[3]) puts("0");
            else if(a[1]<a[2]&&a[2]>a[3]&&a[1]<a[3]) puts("12");
            else if(a[1]>a[2]&&a[2]<a[3]&&a[1]<a[3]) puts("4");
            else if(a[1]<a[2]&&a[2]>a[3]&&a[1]>a[3]) puts("8");
            else if(a[1]>a[2]&&a[2]<a[3]&&a[1]>a[3]) puts("16");
            else if(a[1]>a[2]&&a[2]>a[3]) puts("20");
            continue;
        }//前3个特判了一下,以为会很快,结果和不判一样 
        int minn=0x7fffffff;//还原全排列顺序 
        for(int i=1;i<=n;i++) minn=min(minn,a[i]);minn--;
        for(int i=1;i<=n;i++) a[i]-=minn;
        for(int i=1;i<=n;i++) b[i]=a[i];
        sort(b+1,b+n+1);
        for(int i=1;i<=n;i++){//存储坐标,eg.2 3 1->3 1 2 
            int pos=lower_bound(b+1,b+n+1,a[i])-b;
            c[pos]=i;
        }
        int es=0,ss=0;
        for(int i=1;i<=n;i++) es=es*10+c[i];//数据的顺序 
        for(int i=1;i<=n;i++) ss=ss*10+i;//目标:1234..
        if(ss==es){puts("0");continue;}
        queue<int>q[N];
        if(!vis[ss]){//从目标往回找 
            vis[ss]=1;
            dis[ss]=0;
            q[n].push(ss);
        }
        while(!q[n].empty()){
            int s=q[n].front();q[n].pop();
            memset(b,63,sizeof b);
            int p=w[n],ns=s;
            for(int i=1;i<=n;i++){
                int pos=ns/p;
                ns%=p;p/=10;
                a[i]=pos;
                b[pos]=min(b[pos],i);//当前位置最上面的元素 
            }
            for(int i=1;i<=n;i++){
                int pos=a[i];
                if(b[pos]!=i) continue;//被压在底下 
                if(pos>1&&b[pos-1]>i){//可以往左移 
                    a[i]=pos-1;
                    int ts=0;
                    for(int j=1;j<=n;j++) ts=ts*10+a[j];
                    if(!vis[ts]){
                        vis[ts]=1;
                        dis[ts]=dis[s]+1;
                        q[n].push(ts);
                    }
                }
                if(pos<n&&b[pos+1]>i){//可以往右移 
                    a[i]=pos+1;
                    int ts=0;
                    for(int j=1;j<=n;j++) ts=ts*10+a[j];
                    if(!vis[ts]){
                        vis[ts]=1;
                        dis[ts]=dis[s]+1;
                        q[n].push(ts);
                    }
                }
                a[i]=pos;//还原,因为1->多 
            }
        }
        printf("%d
",dis[es]?dis[es]:-1);
    }
    return 0;
}

青春

【问题描述】

现在有一个被的小格子分割的矩形纸片,每个小格子内包含一个整数。现在你可以进行一系列的折叠,每次折叠的折痕必须是分割两行或者两列小格子的分割线。

在折叠完之后,所有重叠的小格子被看作一个单独的格子,并且这个格子的价值为重叠的小格子的价值和。

你想要知道,在所有可能得到的新格子中,格子价值的最大值为多少。

【输入格式】

输入文件的第一行有两个整数和,分别表示初始的矩形纸片的长和宽。接下来的行,每行有个数字表示初始的小格子内的整数。

【输出格式】

输出一行表示所能得到的格子价值的最大值。

【样例输入】

2 2

1 -2

3 -4

【样例输出】

4

【样例解释】

无。

【数据规模与约定】

对于的数据,格子内的数字权值的绝对值不超过。

数据点

   

数据点

   

1

3

3

6

15

100

2

10

10

7

20

100

3

10

10

8

20

500

4

15

15

9

20

500

5

20

20

10

20

500

 

70分代码(TLE):

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,ans=1<<31,arr[20][500],t[500],dp[500];
void dfs(int p){
    for(int i=0;i<m;i++) t[i]+=arr[p][i];//t[i]表示截止到当前行第i列的所有元素的和 
    for(int j=m-1;~j;j--){
        dp[j]=t[j];//dp[j]表示截止到当前行前j列的元素和最大值 
        ans=max(ans,dp[j]);
        for(int k=0;j+k*2+1<m;k++){//枚举从哪一列对折 
            dp[j]=max(dp[j],t[j]+dp[j+k*2+1]);
            ans=max(ans,dp[j]);
        } 
    }
    for(int j=0;p+j*2+1<n;j++) dfs(p+j*2+1);////枚举从哪一行对折 
    for(int i=0;i<m;i++) t[i]-=arr[p][i];//回溯 
}
int main(){
    freopen("taritari.in","r",stdin);
    freopen("taritari.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            scanf("%d",&arr[i][j]);
    for(int i=0;i<n;i++) dfs(i);
    printf("%d
",ans);
    return 0;
}

100分代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=40;
const int M=510;
const int INF=0x3f3f3f3f;
int n,m,z[N][M],sum[M],f[M],ans;
void solve(int pos){
    for(int a=1;a<=m;a++) sum[a]+=z[pos][a];
    int maxv[2]={-INF,-INF};
    for(int a=1;a<=m;a++){
        f[a]=sum[a];
        if(maxv[(a&1)^1]>0) f[a]+=maxv[(a&1)^1];
        maxv[a&1]=max(maxv[a&1],f[a]);
        ans=max(ans,f[a]);
    }
    for(int a=pos+1;a<=n;a+=2) solve(a);
    for(int a=1;a<=m;a++) sum[a]-=z[pos][a];
}
int main(){
    freopen("taritari.in","r",stdin);
    freopen("taritari.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int a=1;a<=n;a++)
        for(int b=1;b<=m;b++)
            scanf("%d",&z[a][b]);
    ans=-INF;
    memset(sum,0,sizeof sum);
    for(int a=1;a<=n;a++) solve(a);
    printf("%d
",ans);
    return 0;
}

三部曲

【问题描述】

因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为。当城市被加派了名士兵时。城市的所有子城市需要被加派名士兵。这些子城市的所有子城市需要被加派名士兵。以此类推。

当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市i为根的子树中的所有城市共被加派了多少士兵。

你现在是国王的军事大臣,你能回答出国王的每个询问么?

【输入格式】

第一行,包含两个整数代表城市数量以及国王的命令的数量。

第二行个整数,表示号每个节点的父亲节点。

接下来的行,每行代表国王的一个命令,命令分两种:

 在城市加入个士兵

 询问以城市为根的子树中所有士兵数量的和。

【输出格式】

对于每个,输出答案。

【样例输入】

7 10

1 1 2 2 5 5

Q 1

A 2 1

Q 1

Q 2

Q 5

A 5 0

Q 5

A 3 1

Q 1

Q 2

【样例输出】

0

11

11

8

10

14

13

【样例解释】

无。

【数据规模与约定】

对于的数据,。

对于的数据,。

AC代码:

#include<cstdio>
#include<cstring>
#define lc k<<1
#define rc k<<1|1
#define ll long long
#ifdef unix
#define LL "%lld"
#else
#define LL "%I64d"
#endif
using namespace std;
inline const int read(){
    register int x=0;
    register char ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x; 
}
inline const char in(){
    for(register char ch=getchar();;ch=getchar()) if(ch>='A'&&ch<='Z') return ch;
}
const int N=1e5+10;
const ll inf=1e9;
struct node{
    int v,next;
}e[N<<1];
int n,m,tot,it,start[N],end[N],size[N],sum[N],dep[N],head[N];
ll tag[N<<2],a[N<<2],c[N];
void add(int x,int y){
    e[++tot].v=y;
    e[tot].next=head[x];
    head[x]=tot;
}
void build(int k,int l,int r){
    a[k]=-inf;//初始化-∞ 
    if(l==r) return ;
    int mid=l+r>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
}
void dfs(int u,int f){
    start[u]=++it;//遍历起止时间戳 
    size[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==f) continue;
        dep[v]=dep[u]+1;//遍历深度 
        dfs(v,u);
        size[u]+=size[v];//统计子树节点个数 
        sum[u]=sum[u]+sum[v]+size[v];//统计以u为节点的子树的常数 
    }
    end[u]=it;
}
void change(int k,int l,int r,int x,int y,ll w){
    if(l>y||r<x) return ;
    if(l>=x&&r<=y){
        tag[k]++;//打标记 
        if(a[k]==-inf) a[k]=w;
        else a[k]=a[k]+w;
        return ;
    }
    int mid=l+r>>1;
    change(lc,l,mid,x,y,w);
    change(rc,mid+1,r,x,y,w);
}
ll query(int k,int l,int r,int y,int x,ll w,ll num){
    if(a[k]!=-inf){
        if(w==-inf) w=a[k]+tag[k]*dep[x];
        else w=w+a[k]+tag[k]*dep[x];
        num+=tag[k];
    }
    if(l==r){
        if(w==-inf) return 0;
        else return w*size[x]+num*sum[x];
    }
    int mid=l+r>>1;
    if(y<=mid) return query(lc,l,mid,y,x,w,num);
    if(y>mid) return query(rc,mid+1,r,y,x,w,num);
}
inline const int lowbit(int x){return x&-x;}
void addw(int p,ll w){
    for(int i=p;i<=n;i+=lowbit(i)) c[i]+=w;
}
ll getsum(int p){
    ll res=0;
    for(int i=p;i;i-=lowbit(i)) res+=c[i];
    return res;
}
int main(){
    freopen("truetears.in","r",stdin);
    freopen("truetears.out","w",stdout);
    n=read();m=read();
    for(int i=2,x;i<=n;i++) x=read(),add(x,i);
    dep[1]=0;
    dfs(1,0);
    build(1,1,n);
    char ch;
    for(int i=1,x,y;i<=m;i++){
        if((ch=in())=='A'){
            x=read();y=read();
            change(1,1,n,start[x],end[x],y-dep[x]);//因为每层加的都不一样,so要搞深度 
            addw(start[x],(ll)y*size[x]+sum[x]);//树状数组维护区间和(起止时间戳为区间左右端点) 
        }
        else{
            x=read();
            ll ans=query(1,1,n,start[x],x,-inf,0);//求出 标记下放后增加的 
            ans=ans+getsum(end[x])-getsum(start[x]);//再加上 上一次原有的 
            printf(LL"
",ans);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shenben/p/5930738.html