成绩:200

关于考试:
T1:15分(预估100,实际100) AC
T2:30分(预估100,实际0) 文件错误(freopen加了注释)
T3:120分(预估100,实际100) AC

经验教训:做完了先不要急着交,先检查一下输入输出文件,这次的T2由于在freopen上加了注释,所以爆0了,不然AK了……

T1

括号序列(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|是偶数。

思路:自己打表发现就是贪心,用两个计数器来记录左括号和右括号的个数,然后答案就是(num>>1)+(num&1)+(cnt>>1)+(cnt&1),因为有种情况是左边全是右括号。

代码:

#include<cstdio>
#include<cstring>
#define maxn 100007
using namespace std;
int num,cnt,ans;
char s[maxn];
int main() {
  freopen("bracket.in","r",stdin);
  freopen("bracket.out","w",stdout);
  scanf("%s",s+1);
  int len=strlen(s+1);
  for(int i=1;i<=len;++i) {
    if(s[i]=='(') num++;
    else {
      if(s[i]==')'&&num) num--;
      else cnt++;
    }
  }
  ans=(num>>1)+(num&1)+(cnt>>1)+(cnt&1);
  printf("%d\n",ans);
  return 0;
}

T2

公交车(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只,n<=10。
对于另外30%的数据k,n<=1000。
对于100%的数据1<=n<=20000,1<=k<=50000,1<=M<=100,1<=ci<=100,1<=xi<yi<=n。

思路:还是贪心……是一个带权相交线段覆盖问题,对每组怪兽的终点排一遍序,然后能上的就上,到了就下来。

代码:

#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 50007
using namespace std;
int ans,n,m,k,w[maxn];
inline int qread() {
  char c=getchar();int num=0,f=1;
  for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
  for(;isdigit(c);c=getchar()) num=num*10+c-'0';
  return num*f;
}
struct node {
  int u,v,c;
}e[maxn];
const int inf=0x7fffffff;
inline bool cmp(node a, node b) {
  if(a.v!=b.v) return a.v<b.v;
  return a.u<b.u;
}
int main() {
  freopen("bus.in","r",stdin);
  freopen("bus.out","w",stdout);
  k=qread(),n=qread(),m=qread();
  for(int i=1;i<=k;++i) e[i].u=qread(),e[i].v=qread(),e[i].c=qread();
  sort(e+1,e+1+k,cmp);
  for(int i=1;i<=k;++i) {
    if(w[e[i].u]>=m) continue;
    int minn=inf;
    for(int j=e[i].u;j<=e[i].v;++j) {
      minn=min(m-w[j],minn);
      if(minn<=0) break;
    }
    if(minn>0) {
      if(minn>=e[i].c) { 
        for(int j=e[i].u;j<e[i].v;++j)
          w[j]+=e[i].c;
        ans+=e[i].c;
      }
      else {
        for(int j=e[i].u;j<e[i].v;++j)
          w[j]+=minn;
        ans+=minn;
      }
    }
  }
  printf("%d\n",ans);
  return 0;
}

T3

解谜游戏(puzzle)

Time Limit:1000ms Memory Limit:128MB

题目描述

LYK进了一家古董店,它很想买其中的一幅画。但它带的钱不够买这幅画。
幸运的是,老板正在研究一个问题,他表示如果LYK能帮他解出这个问题的话,就把这幅画送给它。
老板有一个n*m的矩阵,他想找一个和最大的子矩阵,这个子矩阵可以由四个参数x,y,x2,y2(1<=x<=x2<=n,1<=y<=y2<=m)来表示,表示一个左上角为(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,m<=10。
对于40%的数据n,m<=25。
对于60%的数据n,m<=50。
对于80%的数据n,m<=100。
对于100%的数据1<=n,m<=300,|P|,|ai,j|<=1000。

思路:如果这个题目只有一维且没有修改的话,那么显然就是一个最大字段和问题,这时候就可以用的dp轻松解决,用f[i]表示前i个数的最大子段和,那么这时候就是f[i]=max(f[i-1]+a[i],a[i])。然后这个题二维的话,用二维前缀和维护即可,然后枚举行,看看每列的最大子矩阵和,修改肯定要修改最小的那个数,因为最小的数对答案贡献最小。

#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 307
using namespace std;
int n,m,zrj[maxn],sum[maxn][maxn],p,a[maxn][maxn],f1[maxn],f2[maxn],cyh[maxn];
inline int qread() {
  char c=getchar();int num=0,f=1;
  for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
  for(;isdigit(c);c=getchar()) num=num*10+c-'0';
  return num*f;
}
int maxx;
int main() {
  freopen("puzzle.in","r",stdin);
  freopen("puzzle.out","w",stdout);
  n=qread(),m=qread(),p=qread();
  for(int i=1;i<=n;++i) {
    for(int j=1;j<=m;++j) {
      sum[i][j]=qread();
      a[i][j]=sum[i][j];
      sum[i][j]+=sum[i-1][j];
    }
  }
  for(int l=1;l<=n;++l) {
    for(int r=1;r<=m;++r) 
      zrj[r]=1020040222;
    for(int j=l;j<=n;++j) {
      for(int k=1;k<=m;++k) {
        zrj[k]=min(zrj[k],a[j][k]);
        cyh[k]=sum[j][k]-sum[l-1][k];
      }
      for(int i=1;i<=m;++i) {
        f1[i]=max(f1[i-1]+cyh[i],cyh[i]);
        f2[i]=max(f1[i-1]+cyh[i]-zrj[i]+p,max(f2[i-1]+cyh[i],cyh[i]-zrj[i]+p));
      }
      if(l==1&&j==n) maxx=max(maxx,f2[m]);
      else for(int i=1;i<=m;++i) maxx=max(maxx,max(f1[i],f2[i]));
    }
  }
  printf("%d\n",maxx);
  return 0;
}