10.3 test solution.

9 篇文章 0 订阅
4 篇文章 0 订阅

1.括号序列(bracket)

Time Limit:

1000ms

Memory Limit:

128MB

题目描述

LYK有一个括号序列,但这个序列不一定合法。
一个合法的括号序列如下:
()是合法的括号序列。
若A是合法的括号序列,则(A)是合法的括号序列。
若A和B分别是合法的括号序列,则AB是合法的括号序列。
LYK想通过尽可能少的操作将这个不一定合法的括号序列变成合法的括号序列。一次修改操作是将某个字符变成另一个字符。
你能帮帮它吗?

输入格式(bracket.in)

一行一个字符串 S

输出格式(bracket.out)

一个数表示最少修改次数。

输入样例

()))

输出样例

1

样例解释

将第二个字符修改成(即可。

数据范围

对于30%的数据 |S|10
对于 60% 的数据 |S|1000
对于 100% 的数据 |S|100000 。且 |S| 是偶数。


solution

  • 把字符串从左往右扫一遍,用一个计数器 x 存当前没有抵消掉的( 的数量

  • 如果是(,如果x=strlen(s)/2,那就 ans++,x ,否则 x++

  • 因为如果括号合法,那() 的数量肯定是 strlen(s)/2

  • 所以如果( 的数量已经是 strlen(s)/2 了,那就只能把他变成) ,变成) 以后,他肯定会抵消掉一个(,所以如果 x=strlen(s)/2 ,那就 ans++,x

  • 如果是),如果 x=0 ,那就 ans++,x++ ,否则 x

  • 因为如果没有() 匹配,那就只能把他变成(

  • 扫完以后, ans 最后还要加上 x/2 ,因为如果还有( 没有抵消,就需要把其中的一半变成) ,所以最后要加上 x/2

code

  • test AC code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100010

char s[MAXN];

int main() {
    freopen("bracket.in","r",stdin);
    freopen("bracket.out","w",stdout);
    scanf("%s",s);
    int n=strlen(s);
    int ans=0,x=0;
    for(int i=0;i<n;i++) {
        if(s[i]=='(')  {
            if(x==n/2) ans++,x--;
            else x++;
        }
        if(s[i]==')') {
            if(x==0) ans++,x++;
            else if(x<=n/2) x--;
        }
    }
    printf("%d",ans+x/2);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

2.公交车(bus)

Time Limit:

1000ms

Memory Limit:

128MB

题目描述

LYK在玩一个游戏。
有k群小怪兽想乘坐公交车。第 i 群小怪兽想从xi出发乘坐公交车到 yi 。但公交车的容量只有 M ,而且这辆公交车只会从1号点行驶到 n 号点。
LYK想让小怪兽们尽可能的到达自己想去的地方。它想知道最多能满足多少小怪兽的要求。
当然一群小怪兽没必要一起上下车,它们是可以被分开来的。

输入格式(bus.in)

第一行三个数k,n,M
接下来k行每行3个数 xi,yi ci 。其中 ci 表示第i群小怪兽的小怪兽数量。

输出格式(bus.out)

一个数表示最多有多少只小怪兽能满足要求。

输入样例

3 5 3
1 3 4
3 5 2
1 5 3

输出样例

5

样例解释

第一群的3只小怪兽在1号点上车,并在3号点下车。
第二群的2只小怪兽在3号点上车,5号点下车。

数据范围

对于 30% 的数据小怪兽的总数不超过 10 只, n10
对于另外 30% 的数据 k,n1000
对于 100% 的数据 1n20000,1k50000,1M100,1ci100,1xi<yin


solution

  • 考虑经典的线段覆盖,是按右端点排序之后,能取就取

  • 所以这些怪兽也是这样,按照右端点排序之后,能取就取,能过几个就过几个

  • 上面那个贪心是正确的,亲测可以AC,证明显然

  • 这样的话我们就需要知道在某个时刻 t ,有多少只小怪兽在车上

  • 所以维护一个数组f[i],表示 i 这个时刻有多少只小怪兽在车上

  • 所以对于排序之后的一个区间,我们可以写出下面的伪代码

for (int i=X; i<Y; i++)
  MAX=max(MAX,f[i]);//在这个区间里找最多有几只小怪兽
t=min(Z,M-MAX);//M-MAX就是最少可以做多少只怪兽,
for (int i=X; i<Y; i++) f[i]+=t;
ans+=t
  • 所以区间加区间求MAX就可以用数据结构来维护,线段树就好了

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

template<typename T>
void input(T &x) {
    x=0; T a=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
        if(c=='-') a=-1;
    for(;c>='0'&&c<='9';c=getchar())
        x=x*10+c-'0';
    x*=a;
    return;
}

#define MAXK 50010
#define MAXN 20010
#define MAXM 110

struct Data {
    int l,r,sum;
    Data(int l=0,int r=0,int sum=0):
        l(l),r(r),sum(sum) {}
    bool operator < (const Data &q) const {
        if(r!=q.r) return r<q.r;
        return l<q.l;
    }
};

Data mon[MAXK];

struct Segment_Tree {
    int l,r,tag,Max;
};

Segment_Tree t[MAXN<<2];

void updata(int now) {
    t[now].Max=max(t[now<<1].Max,t[now<<1|1].Max);
    return;
}

void build(int now,int l,int r) {
    t[now].l=l,t[now].r=r;
    if(l==r) {
        t[now].Max=0;
        return;
    }
    int mid=l+r>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    updata(now);
    return;
}

void pushdown(int now) {
    int &d=t[now].tag;
    t[now<<1].tag+=d;
    t[now<<1|1].tag+=d;
    t[now<<1].Max+=d;
    t[now<<1|1].Max+=d;
    d=0;
    return;
}

void Modify(int now,int L,int R,int x) {
    int l=t[now].l,r=t[now].r;
    if(l==L&&R==r) {
        t[now].tag+=x;
        t[now].Max+=x;
        return;
    }
    if(t[now].tag) pushdown(now);
    int mid=l+r>>1;
    if(R<=mid) Modify(now<<1,L,R,x);
    else if(L>mid) Modify(now<<1|1,L,R,x);
    else {
        Modify(now<<1,L,mid,x);
        Modify(now<<1|1,mid+1,R,x);
    } 
    updata(now);
    return;
}

int query(int now,int L,int R) {
    int l=t[now].l,r=t[now].r;
    if(l==L&&R==r) return t[now].Max;
    if(t[now].tag) pushdown(now);
    int mid=l+r>>1;
    if(R<=mid) return query(now<<1,L,R);
    else if(L>mid) return query(now<<1|1,L,R);
    else {
        int a=query(now<<1,L,mid),
            b=query(now<<1|1,mid+1,R);
        return max(a,b);
    }
}

int main() {
    int k,n,m;
    input(k),input(n),input(m);
    for(int i=1,x,y,c;i<=k;i++) {
        input(x),input(y),input(c);
        mon[i]=Data(x,y-1,c);
    }
    sort(mon+1,mon+k+1);
    build(1,1,n);
    int ans=0;
    for(int i=1,l,r,Max;i<=k;i++) {
        l=mon[i].l,r=mon[i].r;
        Max=min(mon[i].sum,m-query(1,l,r));
        ans+=Max;
        Modify(1,l,r,Max);
    }
    printf("%d\n",ans);
    return 0;
}

3.解谜游戏(puzzle)

Time Limit:

1000ms

Memory Limit:

128MB

题目描述

LYK进了一家古董店,它很想买其中的一幅画。但它带的钱不够买这幅画。
幸运的是,老板正在研究一个问题,他表示如果LYK能帮他解出这个问题的话,就把这幅画送给它。
老板有一个nm的矩阵,他想找一个和最大的子矩阵,这个子矩阵可以由四个参数 x,y,x2,y2 (1xx2n,1yy2m) 来表示,表示一个左上角为 (x,y) ,右下角为 (x2,y2) 的矩阵。
为了让游戏更加有趣,老板给了一个常数 P ,他想将原来这个矩阵中恰好一个数变为P,使得这个矩阵的最大的子矩阵尽可能大。
老板想知道这个最大值是多少。
你能帮帮LYK吗?

输入格式(puzzle.in)

第一行三个数 n,m,P
接下来 n 行,每行m个数 ai,j 描述整个矩阵。

输出格式(puzzle.out)

输出一个数表示答案。

输入样例

3 3 3
-100 3 3
3 -4 3
3 3 3

输出样例

20

样例解释

改变左上角那个数。

数据范围

对于 20% 的数据 n,m10
对于 40% 的数据 n,m25
对于 60% 的数据 n,m50
对于 80% 的数据 n,m100
对于 100% 的数据 1n,m300,|P|,|ai,j|1000


solution

  • 要想会做这个题,首先要会求最大子矩阵

  • 所以先说一下怎么求最大子矩阵

  • 对于每一列,我们做一个前缀和,即 sum[i][j] 表示第 i 行前 j 列的和

  • 然后我们枚举子矩阵的左上角和右下角所在的行

  • 这样问题就变成了最大字段和问题,用一张图来解释

qwq

  • 因为我们预处理了 sum ,所以第 i 行和第 j 行第 k 列的值就可以用sum[j][k]sum[i1][k]来表示

  • 所以 i j 之间的每一列的和就变成了一个值,所以他们就构成了一个序列

  • 而我们要找的就是一个区间 [l,r] 使得区间的和最大,这就是赤裸裸的最大字段和问题

  • 因为求最大字段和的做法太多了,所以就不展开讲啦,推荐一篇blog

  • 会求最大子矩阵之后,再想一个很显然贪心

  • 在一个子矩阵中,修改矩阵中的最小值是最好的

  • 所以我们每次加和都有两种决策,不修改和改最小值

  • 然后利用这个修改一下最大子段和的 dp 就可以了

  • f[i][0] i 结尾并且没有数被修改过的最大和

  • f[i][1] i 结尾并且有数被修改过的最大和

  • f[i][0]显然就是最大子段和

  • 重点是 f[i][1] 怎么更新

  • 修改可能在 i 之前,那f[i][1]就可以用 f[i1][1]+b[i] 更新, b[i] 表示第 i 列的和

  • 修改可能就在 i,那 f[i][1] 就可以用 f[i1][0]+b[i]+PMin[i] 更新,也就是改的话最大子矩阵的和会比不改多 PMin[i] Min 显然是用来存最小值的

  • 当然也有可能第 i 列的和修改以后比之前的和大,那这个时候f[i][1]就可以用 b[i]+PMin[i] 更新

  • 所以综上所述

  • f[i][1]=max{f[i1][1]+b[i],f[i1][0]+b[i]+PMin[i],b[i]+PMin[i]}

  • 但是题目中的要求是必须修改1个数,所以还要考虑一下最后答案怎么更新,具体见代码

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

template<typename T>
void input(T &x) {
    x=0; T a=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
        if(c=='-') a=-1;
    for(;c>='0'&&c<='9';c=getchar())
        x=x*10+c-'0';
    x*=a;
    return;
}

#define MAXN 310
#define MAXM 310

int f[MAXM][2];
int b[MAXM];
int a[MAXN][MAXM];
int sum[MAXN][MAXM];
int Min[MAXM];

int main() {
    int n,m,p;
    input(n),input(m),input(p);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) {
            input(a[i][j]);
            sum[i][j]=sum[i-1][j]+a[i][j];
        }
    int ans=-20001120;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++)
            Min[j]=a[i][j];
        for(int j=i;j<=n;j++) {
            for(int k=1;k<=m;k++)
                Min[k]=min(Min[k],a[j][k]);
            for(int k=1;k<=m;k++)
                b[k]=sum[j][k]-sum[i-1][k];
            f[0][1]=-20001120;
            for(int k=1;k<=m;k++) {
                f[k][0]=max(f[k-1][0]+b[k],b[k]);
                f[k][1]=max(f[k-1][1]+b[k],max(f[k-1][0]+b[k]+p-Min[k],b[k]+p-Min[k]));
            }
            for(int k=1;k<m;k++)
                ans=max(ans,max(f[k][1],f[k][0]));
            if(i==1&&j==n) {
                ans=max(ans,f[m][1]);
                for(int k=m,sum=0;k>1;k--) {
                    sum+=b[k];
                    ans=max(ans,sum);
                }
            } else ans=max(ans,max(f[m][1],f[m][0]));
        }
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值