题目链接https://ac.nowcoder.com/acm/problem/20273
首先我们把这个问题分成两个子问题,然后分别对两个子问题求解,从而解决一个复杂的问题。
我们先来看第一个子问题:
这个子问题比较简单,就是仿照题目的,但是这个子问题里只有一条木板,即给你一条木板,上面有连续的格子,你能涂次求,求涂
次的情况下最多能粉刷正确多少个格子
然后我们来看第二个子问题
我们可以把他想象成以下的模型,你要在个商店中买东西,每个商店只能买一件物品(可以不买),你有
元的钱,
每个商店有种商品,每一件商品的价格为
,价值为
,问你花费最多
元,可以获得商品的价值最大为多少
我们来将两个问题结合一下,那么我们用第一个子问题求出每个木板涂 至
的次数,那么对应的涂的次数就是第二问中商品的价格,粉刷正确的格子个数就是第二问中商品的价值。
第二问求出的最大价值就是题目所求的最大正确粉刷格子数
这两个问题都是经典的dp问题,我们先来看第一个
开一个三维的dp数组, 第一维表示涂到第几个格子,第二维表示涂的次数,第三维表示当前涂的是
还是
转移方程很显然是从前一个继续涂或者重新开始涂转移过来
即
如果这一个涂对了就要对对应涂对的状态,比如当前是
就要对
对于第二个问题就是经典的背包问题,这里就不多讲了
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; int dp[55][55][2]; int dp2[55][2505]; char s[55]; int n,m,t; vector<int> v[55];//v[i][j]表示第i个木板涂j次最大粉刷正确的格子数 int main() { ios::sync_with_stdio(false); cin>>n>>m>>t; for(int x=1;x<=n;x++) { cin>>s+1; for(int i=1;i<=m;i++) s[i]-='0'; memset(dp,0,sizeof(dp)); for(int i=1;i<=m;i++) { for(int j=1;j<=i;j++) { dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j-1][1]); dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]); dp[i][j][s[i]]++;//涂对了 } } v[x].push_back(0); for(int i=1;i<=m;i++) { v[x].push_back(max(dp[m][i][0],dp[m][i][1])); } } for(int i=1;i<=50;i++) { for(int j=0;j<=t;j++) { for(int k=0;k<=min((int)v[i].size()-1,j);k++) { dp2[i][j]=max(dp2[i][j],dp2[i-1][j-k]+v[i][k]); } } } cout<<dp2[n][t]<<endl; return 0; }