刷题记录(NC20273 [SCOI2009]粉刷匠,NC210249 打砖块(brike))

NC20273 [SCOI2009]粉刷匠

题目链接

关键点:

1、题目要求求出给出的粉刷次数下,所能正确粉刷的木板的格子的最多数量

2、我们设g[i][j]表示粉刷前i个木板,粉刷j次所能粉刷最多的正确格子数,那么我们就得知道对于每一个木板粉刷k次所能粉刷最多正确格子数

3、设f[i][j][k],表示对于第i个木板,粉刷j次,粉刷前k个格子所能得到的最多正确的格子数

f[i][j][k] = max(f[i][j-1][l] + w(l+1, k));//枚举l为上一次粉刷的格子数,w(l+1, k)指当前l+1到k一次粉刷能得到的最多的正确格子数

4、如果维护w(l+1, k),我们可以求出蓝色格子的前缀和,设sum[i][j]为粉刷第i个木板,前j个格子的蓝色格子和,那么w(l+1, k)(第i个木板) == max(sum[i][k]-sum[i][l], k-l-sum[i][k]+sum[i][l])

5、对于g[i][j] = max(g[i-1][k] + f[i][j-k][m]);//k为枚举上一块木板粉刷的次数

6、答案即为对于所有木板的各种粉刷次数组合中,所能得到的答案的最大值

完整代码:

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll g[60][2510];//前i个木板粉刷j次能正确粉刷的格子数
ll f[60][2510][60];//第i个木板,粉刷j次,前k个格子能正确粉刷的格子数
int n, m, t;
ll sum[60][60];
int main()
{
    string s;
    cin>>n>>m>>t;
    for (int i=1; i<=n; i++)
    {
        cin>>s;
        for (int j=0; j<s.size(); j++)
        {
            if (s[j] == '1')
                sum[i][j+1] = sum[i][j]+1;
            else
                sum[i][j+1] = sum[i][j];
//             cout<<sum[i][j]<<" ";
        }
//         cout<<endl;
    }
    for (int i=1; i<=n; i++)//第i个木板
    {
        for (int j=1; j<=t; j++)//前j次
        {
            for (int k=1; k<=m; k++)//前K个格子
            {
                for (int l=0; l<=k; l++)//枚举j-1次粉刷的格子数
                f[i][j][k] = max(f[i][j][k], f[i][j-1][l] + 
                                 max(sum[i][k]-sum[i][l], k-l-sum[i][k]+sum[i][l]));
            }
        }
    }
    ll a = 0;
    for (int i=1; i<=n; i++)//前i个木板
    {
        for (int j=1; j<=t; j++)//前j次粉刷
        {
            for (int k=0; k<=j; k++)//枚举第i-1个木板的粉刷次数
            {
                g[i][j] = max(g[i][j], g[i-1][k]+f[i][j-k][m]);
                a = max(a, g[i][j]);
            }
        }
    }
    cout<<a<<endl;
    return 0;
}

NC210249 打砖块(brike)

题目链接

关键点:

1、对于第(i, j)个砖块要被打掉,那么其(i-1, j),(i-1, j+1)砖块也要被打掉,可以发现对于被打掉的砖块,可以形成一个以斜等腰直角三角形,并且,对于越处于底下(行数大)的砖块,其形成的等腰直角三角形的底边就越大

2、因此,对于其底边(即题目给出的二维数组的斜边)的递增现象,我们可以设状态f[i][j][k],表示前i条斜边,第i条斜边敲掉j个砖头,总共敲了k个砖头的最多的值,则

f[i][j][k] = max(f[i-1][l][j-k]);//l为枚举第i-1条斜边所敲掉的砖头

3、对于每一个的取值范围,首先是j,第i条斜边,能敲掉最多i个砖头,因此j = (0, i)

对于k,第i条边一定敲掉j个砖头,因此k(j, m)

对于l,因为对于越往外的斜边,其外层被敲掉的砖头,内层的砖头一定要大于等于外层砖头-1,来保持斜直角等腰三角形的性质,因此l = max(0, j-1)

4、答案为每次枚举k为m时,对最多的取值来取一个最大值

5、初始化,因为砖头的价值是<50,说明其可能为负数,因此初始化一个负无穷,对于什么都不选,则价值为0,f[0][0][0] = 0;

完整代码:

# include <bits/stdc++.h>
using namespace std;
int n, m;
int f[60][510][510];//敲掉前i列,第i列k块砖,j块砖头
int a[50][50];
int sum[50][50];//第i个斜边,前j行砖块的前缀和
int main()
{
    cin>>n>>m;
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=n-i+1; j++)
        {
            cin>>a[i][j];
        }
    }
    for (int i=1; i<=n; i++)//第i条斜边
    {
        for (int j=i, k=1; j>=1; j--, k++)//j=列,k=行
        {
            sum[i][k] = sum[i][k-1]+a[k][j];
//             cout<<sum[i][j]<<" ";
        }
//         cout<<endl;
    }
    memset(f, -0x3f, sizeof(f));
    f[0][0][0] = 0;
    int ans = 0;
    int i, j, k;
    for (i=1; i<=n; i++)//前i个斜边
    {
        for (j=0; j<=i; j++)//第i个斜边,选前j个砖块
        {
            for (k=j; k<=m; k++)//总共选了k个砖块
            {
                for (int l=max(0, j-1); l<=i-1; l++)//第i-1个斜边选了l个砖块
                    f[i][j][k] = max(f[i][j][k], f[i-1][l][k-j]+sum[i][j]);
                if (k==m)
                ans = max(ans, f[i][j][k]);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值