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;
}