链接:http://codeforces.com/problemset/problem/946/D
题意:有n天,每天m节课,总共可以逃k节课,问怎么逃课能使上课时间最短,并输出。
思路:按多重背包的思路来考虑,dp[i][j]表示,对于前i天如果第i天逃j节课能少上的时间是多少。
vv[i][j]代表对于第i天,如果逃j节课最多能少上的时间。
sum[i]代表第i天不逃课要上的时间,z是第i天如果逃z节课。
转移方程:dp[i][j]=max(dp[i][j],dp[i-1][j-z]+vv[i][z]);
代码:
#include<bits/stdc++.h>
using namespace std;
char s[505][505];
int v[505];
int sum[505];
int vv[505][505];
int dp[505][505];
int main()
{
int n,m,k;
int cnt=0;
int sum_time=0;
memset(sum,0,sizeof(sum));
scanf("%d%d%d",&n,&m,&k);
memset(vv,0,sizeof(vv));
for(int i=0;i<n;i++)
{
scanf("%s",s[i]);
int cnt=0;
memset(v,0,sizeof(v));
for(int j=0;j<m;j++)
{
if(s[i][j]=='1') v[cnt++]=j;
}
sum[i]=cnt;
if(!cnt) continue;
sum_time+=(v[cnt-1]-v[0]+1);
for(int j=1;j<=k;j++) //v[i][j] 第i行逃j门课时减少的时间
{
if(j>=cnt) {
vv[i][j]=v[cnt-1]-v[0]+1;continue;
}
int left=cnt-j;
int all=v[cnt-1]-v[0]+1;
if(left==0) {vv[i][j]=all;continue;}
for(int z=0;z+left<=cnt;z++)
{
vv[i][j]=max(vv[i][j],v[z]-v[0]+v[cnt-1]-v[z+left-1]);
}
}
}
// for(int i=0;i<n;i++)
// {
// for(int j=0;j<m;j++)
// {
// printf(j==m-1?"%d\n":"%d ",vv[i][j]);
// }
// }
memset(dp,0,sizeof(dp));
for(int i=0;i<=k;i++) {dp[0][i]=vv[0][i];}
for(int i=1;i<n;i++) //前i天
{
for(int j=k;j>=0;j--) //逃j节课
{
for(int z=0;z<=j&&z<=sum[i];z++) //假设第i天如果逃z节课
{
dp[i][j]=max(dp[i][j],dp[i-1][j-z]+vv[i][z]);
}
}
}
cout<<sum_time-dp[n-1][k]<<endl;
}