题目链接:点击打开链接
题意:某大学一周有n天,一天有m小时工作/上课时间。给定n行m列0 和 1组成的字符,当第i行第j列为1时表示第i天第j小时有课。主角每天在学校的时间为第一节课到最后一节课之间(包括这两节课)的所有时间。现在主角想翘课,但一周最多翘k节课,那么现在主角就可以在翘课数小于等于k时选择自己想上的第一节课和最后一节课,主角当天在学校的时间则为这两节课之间(包括这两节课)的所有时间。问主角一周最少在学校呆多久。
思路:非常明显的DP问题。不妨设dp [ i, j ] 为前i天翘j节课最多能在原来的基础上少呆在学校的时间。每一天只能选择翘 0 - 当天课的总数门课。我们先计算出第i天翘j节课最多能少呆在学校的时间,不妨设其为w [ i, j ]。那么问题就类似于背包问题。第i天时只能选择翘 0节课、1节课 ... 最多节课,那么递推公式就是
for t 0 to k :
for j 0 to maxntoday && j+t<=k:
dp[i, t+j] = max( dp[i, t+j] , dp[i-1, t] + w[i, j])
最后输出dp [n, k] 即可。
AC代码如下: DP Version 1
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <string>
using namespace std;
#define FSIO ios::sync_with_stdio(0);cin.tie(0);
#define DEBUG(a) cout<<"DEBUG: "<<(a)<<endl;
const int MAXN = 505;
int bagg[3][MAXN][MAXN];
int dp[MAXN][MAXN];
string tmp;
int n, m, k;
int main()
{
FSIO;
while(cin>>n>>m>>k)
{
memset(bagg,0,sizeof(bagg));
int cnt0, cnt1;
int summ = 0;
for(int i=1;i<=n;++i)
{
cnt0=cnt1=0;
cin>>tmp;
int st=0;
int nail = tmp.length()-1;
while(nail>=0&&tmp[nail]=='0') nail--;
while(st<tmp.length()&&tmp[st]=='0') st++;
for(int j=st;j<=nail;++j)
{
if(tmp[j]=='0') cnt0++;
else cnt1++;
bagg[1][i][cnt1] = cnt1+cnt0;
}
cnt0 = 0;
cnt1 = 0;
for(int j=nail;j>=st;--j)
{
if(tmp[j]=='0') cnt0++;
else cnt1++;
bagg[2][i][cnt1] = max(bagg[2][i][cnt1],cnt0+cnt1);
}
bagg[0][i][cnt1+1] =bagg[1][i][cnt1+1] = bagg[2][i][cnt1+1] = -1;
for(int s=0;bagg[1][i][s]!=-1;++s)
{
for(int t=0;bagg[2][i][t]!=-1&&s+t<=cnt1&&bagg[1][i][s]+bagg[2][i][t]<=cnt0+cnt1;++t)
{
bagg[0][i][s+t] = max(bagg[0][i][s+t],bagg[1][i][s]+bagg[2][i][t]);
}
}
summ += cnt0 + cnt1;
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;++i)
{
for(int t=0;t<=k;++t)
for(int j=0;bagg[0][i][j]!=-1&&j+t<=k;++j)
dp[i][t+j] = max(dp[i][t+j],dp[i-1][t]+bagg[0][i][j]);
}
cout<<summ-dp[n][k]<<endl;
}
return 0;
}
模仿背包问题形式的DP Version 2:
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <string>
using namespace std;
#define FSIO ios::sync_with_stdio(0);cin.tie(0);
#define DEBUG(a) cout<<"DEBUG: "<<(a)<<endl;
const int MAXN = 505;
int bagg[3][MAXN][MAXN];
int dp[MAXN][MAXN];
string tmp;
int n, m, k;
int main()
{
FSIO;
while(cin>>n>>m>>k)
{
memset(bagg,0,sizeof(bagg));
int cnt0, cnt1;
int summ = 0;
for(int i=1;i<=n;++i)
{
cnt0=cnt1=0;
cin>>tmp;
int st=0;
int nail = tmp.length()-1;
while(nail>=0&&tmp[nail]=='0') nail--;
while(st<tmp.length()&&tmp[st]=='0') st++;
for(int j=st;j<=nail;++j)
{
if(tmp[j]=='0') cnt0++;
else cnt1++;
bagg[1][i][cnt1] = cnt1+cnt0;
}
cnt0 = 0;
cnt1 = 0;
for(int j=nail;j>=st;--j)
{
if(tmp[j]=='0') cnt0++;
else cnt1++;
bagg[2][i][cnt1] = max(bagg[2][i][cnt1],cnt0+cnt1);
}
for(int s=0;bagg[1][i][s]!=-1;++s)
{
for(int t=0;bagg[2][i][t]!=-1&&s+t<=cnt1&&bagg[1][i][s]+bagg[2][i][t]<=cnt0+cnt1;++t)
{
bagg[0][i][s+t] = max(bagg[0][i][s+t],bagg[1][i][s]+bagg[2][i][t]);
}
}
summ += cnt0 + cnt1;
bagg[0][i][cnt1+1] =bagg[1][i][cnt1+1] = bagg[2][i][cnt1+1] = -1;
//cout<<summ<<endl;
}
/*for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
cout<<bagg[0][i][j]<<" ";
cout<<endl;
}*/
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;++i)
{
for(int t=0;t<=m;++t) dp[i][t] = dp[i-1][t];
for(int j=0;bagg[0][i][j]!=-1;++j)
{
for(int t=j;t<=k;++t)
dp[i][t] = max(dp[i][t],dp[i-1][t-j]+bagg[0][i][j]);
}
//cout<<dp[i][k]<<endl;
}
/*for(int i=1;i<=n;++i)
{
for(int j=1;j<=k;++j)
cout<<dp[i][j]<<" ";
cout<<endl;
}*/
cout<<summ-dp[n][k]<<endl;
}
return 0;
}