RUC2024《综合设计》期中测试

T1
原题链接https://www.luogu.com.cn/problem/P1025
不是我出的

T2
原题链接:https://www.luogu.com.cn/problem/P26787
这道题就是讲过的二分+贪心,先二分规定每两个点之间都必须大于等于某个值,然后依次枚举通过贪心求出最少需要删除的点数,最后用这个最小值和m比较一下即可。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
    x=0;int f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while(isdigit(s))x=x*10+s-48,s=getchar();
    x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
    if(x<0)putchar('-'),x=-x;
    do{buf[++cc]=int(x%10);x/=10;}while(x);
    while(cc)putchar(buf[cc--]+'0');
}
const int N=5e4+10;
int L,n,m,a[N];
bool check(int lim){
  int last=0,cnt=0;
  rep(i,1,n){
    if(a[i]-last<lim)cnt++;
    else last=a[i];
  }
  if(L-last<lim)cnt++;
  return cnt<=m;
}
void solve(){
  qr(L),qr(n),qr(m);
  rep(i,1,n)qr(a[i]);
  int l=1,r=L,mid,ans=0;
  while(l<=r){
    mid=(l+r)/2;
    if(check(mid))l=mid+1,ans=mid;
    else r=mid-1;
  }
  qw(ans);
}
int main(){
    int tt;tt=1;
    while(tt--)solve();
    return 0;
}

T3
原题链接:https://www.luogu.com.cn/problem/P1149
不是我出的

T4
高中的时候师兄出的题,不过我把简单版拿出来考大家,原来的版本不止一块糕,有兴趣的同学可以思考。

猜测有不少人会用能切多大切多大的策略,但是这样确实是不正确的,提交发现答案错误以后应该是可以发现的。

正确的思路就是先考虑 n ∗ 1 n*1 n1 1 ∗ m 1*m 1m这两种糕,这两种糕显然都满足前面那个贪心,这就说明如果不横切只竖切的话,切法是固定的。

观察可以发现,每切一次一边的长度大概会减少一半,也就是说横切和竖切的刀数最多也就是30~40,如果超过了就会切成长度为1的。

结合这两种思路,我们枚举横切的刀数,剩下的刀数全部用来竖切,拿一个数组记录每一边切多少刀以后还会剩下多少,然后统计即可。时间复杂度为 O ( T l o g N ) O(TlogN) O(TlogN)

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
    x=0;int f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while(isdigit(s))x=x*10+s-48,s=getchar();
    x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
    if(x<0)putchar('-'),x=-x;
    do{buf[++cc]=int(x%10);x/=10;}while(x);
    while(cc)putchar(buf[cc--]+'0');
}
const int N=110;
int n,m,k;
int cnt1,f[N];
int cnt2,g[N]; 
void solve(){
  qr(n),qr(m),qr(k);
  f[cnt1=0]=n;
  while(f[cnt1]>1){
    cnt1++;
    f[cnt1]=(f[cnt1-1]+1)/2;
  }
  g[cnt2=0]=m;
  while(g[cnt2]>1){
    cnt2++;
    g[cnt2]=(g[cnt2-1]+1)/2;
  }
  ll ans=0;
  rep(i,0,min(cnt1,k)){
    int j=min(cnt2,max(0,k-i));
    ans=max(ans,1ll*n*m-1ll*f[i]*g[j]);
  }
  qw(ans);puts("");
}
int main(){    int tt;qr(tt); 
    while(tt--)solve();
    return 0;
}

T5
fyh大神出的
原题链接:https://www.luogu.com.cn/problem/P3974
这是fyh大神的思路

#include <cstdio>
const int CN = 1010;
int T, N, M, a[CN][CN];
long long f[CN][CN];
long long max(long long a, long long b, long long c) {
  if (a > b)
    return a > c ? a : c;
  return b > c ? b : c;
}
int main() {
  scanf("%d", &T);
  while (T--) {
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= N; ++i)
      for (int j = 1; j <= M; ++j)
        scanf("%d", &a[i][j]);
    for (int i = N; i >= 1; --i)
      for (int j = 1; j <= M; ++j)
        f[i][j] = max(f[i + 1][j - 1] + a[i][j], f[i + 1][j], f[i][j - 1]);
    printf("%lld\n", f[1][M]);
    for (int i = 1; i <= N; ++i)
      for (int j = 1; j <= M; ++j)
        f[i][j] = 0;
  }
  return 0;
}

洛谷上应该有这种做法的讲解,说实话我也感觉很震惊,也不太理解。

下面讲一下我的贪心法,这种做法实际上就是一行一行模拟路径的选择,大家可以自己模拟一下,这个模拟过程确实挺复杂的。

首先考虑一行的情况,很明显就是取最大值。

然后考虑两行的情况,我的思路是在第一行已经确定路径的基础上将第二行加上,如果只靠第一行的路径没有办法满足第二行的全部需求,那么我们再给第二行添上能够恰好满足条件的路径,这些路径显然是先下到第二行再往右边走。对于第一行原本的路径,我采用的是能往下就往下的做法,比如当前已经有x跳路径到达 ( 1 , i ) (1,i) (1,i),但是 ( 1 , i ) (1,i) (1,i)右边的最大值mx小于x,那么就可以把x-mx的路径提前往下走,剩下的路径显然是可以满足要求的。

现在拓展到一般情况,假设现在要把第i行插入矩阵,其中 f [ i ] [ j ] f[i][j] f[i][j]表示刚好到达 ( i , j ) (i,j) (i,j)的路径条数,这些路径刚好就是从第 i − 1 i-1 i1行直接下来的。

那么我们马上就要考虑需要额外引入多少条路径,这些路径就是从起点直接下到 ( i , 1 ) (i,1) (i,1)。这个值是 m a x ( 0 , m a x ( a [ i ] [ j ] − ∑ k = 1 j f [ i ] [ k ] ) ) max(0,max(a[i][j]-\sum_{k=1}^jf[i][k])) max(0,max(a[i][j]k=1jf[i][k])),也就是最糟糕的情况是把下来的全部路径拉到右边,如果这不能满足要求,那就要额外引入路径。

然后的操作就是从左往右枚举点 ( i , j ) (i,j) (i,j),显然我们要在每一个点都让最多的路径往下,使得剩下的路径在全部往右走的情况下能够满足每一个点的要求,具体实现大家可以自己去想,不过思路都是一样的,这里只是提供其中实现方法。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
    x=0;int f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while(isdigit(s))x=x*10+s-48,s=getchar();
    x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
    if(x<0)putchar('-'),x=-x;
    do{buf[++cc]=int(x%10);x/=10;}while(x);
    while(cc)putchar(buf[cc--]+'0');
}
const int N=1e3+10;
const ll inf=1e18;
int n,m,a[N][N];
ll f[N][N],sum[N][N],nd[N][N],mx[N][N];
void solve(){
  qr(n),qr(m);
  rep(i,1,n)rep(j,1,n)qr(a[i][j]);
  rep(i,1,n)rep(j,1,n)f[i][j]=sum[i][j]=0;
  ll ans=0;
  rep(i,1,n){
    rep(j,1,m){
      sum[i][j]=sum[i][j-1]+f[i][j];
      nd[i][j]=a[i][j]-sum[i][j];
    }
    mx[i][m+1]=-inf;
    dwn(j,m,1)mx[i][j]=max(mx[i][j+1],nd[i][j]);
    ans+=max(0ll,mx[i][1]);
    ll add=max(0ll,mx[i][1]);
    rep(j,1,m){
      if(add>mx[i][j+1]){
        ll maxflow=min(add+sum[i][j],add-mx[i][j+1]);
        f[i+1][j]+=maxflow;
        add-=maxflow;
      }
    }
  }
  qw(ans);puts("");
}
int main(){
    int tt;qr(tt);
    while(tt--)solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值