逃课计划(动态规划)

题目

描述

你是一个大学的学生,你有 n个长度为 m 的字符串,表示你 n 天的课程安排以及每天有m个小时。

字符为 1 表示你需要上这节课,字符为 0 表示你不需要上这节课。假如每天你的第一堂课是在第 i 个小时,而最后一堂课是在第 j 个小时,那么你在这一天会在大学里花 j- i + 1 个小时。如果某天没有课程,那么你会呆在家里,因此要花 0 个小时在大学里。

但是你不喜欢在学校,所以你可以最多逃掉 k节课(你可以任意安排逃掉哪些课,但不能超过 k 节),求你在学校的最少时间。

输入

第一行包含三个整数 n,m和 k( 1≤n,m≤500,0≤k≤500),天数、每天的工作小时数以及你可以逃课的次数。

然后,n 行,第 i 行包含 m 个字符的二进制字符串。如果第 i 行中的第 j 个字符为 1 ,则你在第 i 天的第 j 个小时有一个课程(如果为0,则没有此类课程)。

输出

打印一个整数–表示你在学校的最少时间

样例

输入

2 5 1
01001
10110

输出

5

提示

【样例解释】

你可以逃掉第一天的最后一节课,这样你第一天在学校 11 小时,第二天在学校 44 小时,答案为 55 小时

【数据说明】

对于30%的数据: 1 ⩽ n , m ⩽ 10 1 \leqslant{n,m}\leqslant10 1n,m10

对于60%的数据: 1 ⩽ n , m ⩽ 100 1 \leqslant{n,m}\leqslant100 1n,m100

对于100%的数据: 1 ⩽ n , m ⩽ 500 1 \leqslant{n,m}\leqslant500 1n,m500

分析

令 num[i][j] 代表第 i 天的翘 j 节课的的最小在校时间
令 dp[i][j] 代表前 i 天翘 j 节课的最小在校时间
num[i][j]可以用三层for循环暴力求解
dp[i][j]=min(dp[i][j]=min(dp[i-1][j-o]+num[i][o])

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll mod=1e9+7;

ll dp[600][600],num[600][600];
vector<int>v[600];
void solve()
{
	ll n,m,k,i,j;
	cin>>n>>m>>k;
	for(i=1;i<=n;i++)
	{
		char ch[600];
		cin>>ch;
		for(j=0;j<m;j++)//取出为1的下标 
		{
			if(ch[j]=='1') v[i].push_back(j);
		}
	}
	for(i=1;i<=n;i++) //num[i][j]表示第i天翘j门课的最少在校时间 
	{
		int tmp=v[i].size();
		for(int len=1;len<=tmp;len++)//len代表上课的门数 
		{
			int mm=v[i][tmp-1]-v[i][0]+1; 
			for(j=0;j+len-1<tmp;j++)
			{
				mm=min(mm,v[i][j+len-1]-v[i][j]+1);
			}
			num[i][tmp-len]=mm;
		}
	}
	//dp[i][j]代表前i天翘j门课的最小在校时间 
	//dp[i][j]=min(dp[i-1][j-o]+num[i][o]) 
	for(i=1;i<=n;i++) dp[i][0]=dp[i-1][0]+num[i][0];//初始化翘0节课的在校时间 
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=k;j++) 
		{
			ll mm=0x3f3f3f3f;
			for(int o=0;o<=j;o++)
			{
				mm=min(mm,dp[i-1][j-o]+num[i][o]);
			}
			dp[i][j]=mm;
		}
	}
	cout<<dp[n][k]<<endl;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值