Educational Codeforces Round 39 (Rated for Div. 2) D Timetable

29 篇文章 0 订阅
5 篇文章 0 订阅

题目链接:点击打开链接

题意:某大学一周有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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值