1.括号序列(bracket)
Time Limit:
1000ms
Memory Limit:
128MB
题目描述
LYK有一个括号序列,但这个序列不一定合法。
一个合法的括号序列如下:
()是合法的括号序列。
若A是合法的括号序列,则(A)是合法的括号序列。
若A和B分别是合法的括号序列,则AB是合法的括号序列。
LYK想通过尽可能少的操作将这个不一定合法的括号序列变成合法的括号序列。一次修改操作是将某个字符变成另一个字符。
你能帮帮它吗?
输入格式(bracket.in)
一行一个字符串 S
输出格式(bracket.out)
一个数表示最少修改次数。
输入样例
()))
输出样例
1
样例解释
将第二个字符修改成(即可。
数据范围
对于
对于
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
群小怪兽想从
LYK想让小怪兽们尽可能的到达自己想去的地方。它想知道最多能满足多少小怪兽的要求。
当然一群小怪兽没必要一起上下车,它们是可以被分开来的。
输入格式(bus.in)
第一行三个数
接下来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
只,
n≤10
。
对于另外
30%
的数据
k,n≤1000
。
对于
100%
的数据
1≤n≤20000,1≤k≤50000,1≤M≤100,1≤ci≤100,1≤xi<yi≤n
。
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能帮他解出这个问题的话,就把这幅画送给它。
老板有一个
为了让游戏更加有趣,老板给了一个常数
P
,他想将原来这个矩阵中恰好一个数变为
老板想知道这个最大值是多少。
你能帮帮LYK吗?
输入格式(puzzle.in)
第一行三个数
n,m,P
。
接下来
n
行,每行
输出格式(puzzle.out)
输出一个数表示答案。
输入样例
3 3 3
-100 3 3
3 -4 3
3 3 3
输出样例
20
样例解释
改变左上角那个数。
数据范围
对于
20%
的数据
n,m≤10
。
对于
40%
的数据
n,m≤25
。
对于
60%
的数据
n,m≤50
。
对于
80%
的数据
n,m≤100
。
对于
100%
的数据
1≤n,m≤300,|P|,|ai,j|≤1000
。
solution
要想会做这个题,首先要会求最大子矩阵
所以先说一下怎么求最大子矩阵
对于每一列,我们做一个前缀和,即 sum[i][j] 表示第 i 行前
j 列的和然后我们枚举子矩阵的左上角和右下角所在的行
这样问题就变成了最大字段和问题,用一张图来解释
因为我们预处理了 sum ,所以第 i 行和第
j 行第 k 列的值就可以用sum[j][k]−sum[i−1][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[i−1][1]+b[i] 更新, b[i] 表示第 i 列的和修改可能就在
i ,那 f[i][1] 就可以用 f[i−1][0]+b[i]+P−Min[i] 更新,也就是改的话最大子矩阵的和会比不改多 P−Min[i] , Min 显然是用来存最小值的当然也有可能第 i 列的和修改以后比之前的和大,那这个时候
f[i][1] 就可以用 b[i]+P−Min[i] 更新所以综上所述
f[i][1]=max{f[i−1][1]+b[i],f[i−1][0]+b[i]+P−Min[i],b[i]+P−Min[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;
}