【动态规划】木马级守护进程

木马级守护进程木马级守护进程 


 【问题描述】 
     “哈哈哈,大功告成,木马级守护进程,欢迎来破。”欧教如是说。 
    欧教自从使用了新的教师端守护进程之后,要发现在机房偷偷玩游戏的同学简直是易如 
反掌。对于欧教而言,要使打游戏的同学改邪归正的最好办法就是在他打游戏的时候通过教 
师端关掉他的电脑。 
    但是由于欧教的关机程序写的不好,因此如果要关机,就必须关掉机房中一个矩形区域 
的所有电脑(矩形大小任意),而且在使用了k 次关机程序之后,欧教的电脑就会自动崩溃。 
    机房是一个矩形区域,有n 行m 列。每台电脑都有一个关机价值,当然机房还有很少 
的一部分同学没有玩游戏,因此他们的关机价值就为负数。由于欧教要写木马级关机进程, 
因此只有麻烦乐乐帮他算出在使用k 次关机程序后所能得到的最大关机价值和。 


 【输入数据】 
第一行三个整数n,m,k。 
接下来n 行每行m 个整数,表示每台电脑的关机价值。 


 【输出数据】 
一个整数,表示最大关机价值和。 


 【样例输入】 
4 2 2 
-4 -1 
22 18 
-1 -4 
30 10 


 【样例输出】 
80 


 【数据范围及约定】 
对于40%    的数据:m=1; 
对于100%的数据:1≤n≤100,1≤m≤2,1≤k≤10。 
k 次关机的区域可以重叠,但价值只算一次。 
答案和中间值都在longint 范围内。 



注意1≤m≤2。所以这道题可以拆成两道题来做。

因为这道题WZZ把我们洗涮了一顿,说这道题不会做就不要搞省选了。但是抱怨过去有什么用呢?


m=1时,确实是很简单的一道动规f[i][j]表示前i次关机关前j台电脑的最大价值和

f[ i ][ j ] = max ( f[ i - 1 ][ j ] , f[ k ][ j - 1 ] + sum[ i ] - sum[ k ])

m=2时,要复杂一点,但是也不到做不起的地步。

f[ i ][ j ] = max(f[ k ][ j - 1 ] + sum1[ i ] - sum1[ k ] + sum2[ i ] - sum2[ k ]

,f[ k ][ j - u - v ] + x[ k ][ i ][ u ] + y[ k ][ i ][ v ])

需要预处理维护处x[i,j,k]和y[i,j,k],即[i,j]这个区间内,关机k次左边和右边分别的最大的价值和。

f[ i ][ j ]只需要分别讨论最后一个关机区域只在左边,只在右边,和覆盖了左边右边三种情况。


#include <cstdio>
#include <cstdlib>

long n;long m;long t;
long f[102][12];
long x[102][102][12];
long y[102][102][12];
long v[102][4];
long sum1[102];
long sum2[102];

inline long max(long a,long b)
{
    if (a > b)
        return a;
    return b;
}

int main()
{
    freopen("mua.in","r",stdin);
    freopen("mua.out","w",stdout);
    scanf("%ld%ld%ld",&n,&m,&t);
    for (long i=1;i<n+1;i++)
    {
        for (long j=1;j<m+1;j++)
        {
            scanf("%ld",&v[i][j]);
        }
        sum1[i] = sum1[i-1]+v[i][1];
        sum2[i] = sum2[i-1]+v[i][2];
    }

    if (m == 1)
    {
        for (long i=1;i<n+1;i++)
        {
            for (long j=1;j<t+1;j++)
            {
                for (long k=1;k<i;k++)
                    f[i][j] = max(f[i][j],f[k][j-1]+sum1[i]-sum1[k]);
                f[i][j] = max(f[i][j],f[i-1][j]);
            }
        }
        printf("%ld",f[n][t]);
        return 0;
    }
    if (m == 2)
    {
        for (long l=1;l<n+1;l++)
        {
            for (long i=l;i<n+1;i++)
            {
                for (long j=1;j<t+1;j++)
                {
                    for (long k=l;k<i;k++)
                    {
                        x[l][i][j] = max(x[l][i][j],x[l][k][j-1]+sum1[i]-sum1[k]);
                        y[l][i][j] = max(y[l][i][j],y[l][k][j-1]+sum2[i]-sum2[k]);
                    }
                    x[l][i][j] = max(x[l][i][j],x[l][i-1][j]);
                    y[l][i][j] = max(y[l][i][j],y[l][i-1][j]);
                }
            }
        }
        long ans = 0;
        for (long i=1;i<n+1;i++)
        {
            for (long j=1;j<t+1;j++)
            {
//                for (long k=1;k<i;k++)
//                {
//                    f[i][j] = max(f[i-1][j],f[k][j-1]+sum1[i]-sum1[k]+sum2[i]-sum2[k]);
//                    for (long u=0;u<j+1;u++)
//                    {
//                        for (long v=0;v<j-u+1;v++)
//                        {
//                            f[i][j] = max(f[i][j],f[k][j-u-v]+x[k][i][u]+y[k][i][v]);
//                        }
//                    }
//                }
                f[i][j] = f[i-1][j];
                for (long k=1;k<i;k++)
                {
                    f[i][j] = max(f[i][j],f[k][j-1]+sum1[i]-sum1[k]+sum2[i]-sum2[k]);
                    for (long u=0;u<j+1;u++)
                    {
                        for (long v=0;v<j-u+1;v++)
                        {
                            f[i][j] = max(f[i][j],f[k][j-u-v]+x[k][i][u]+y[k][i][v]);
                        }
                    }
                }
                ans = max(ans,f[i][j]);
            }
        }
        printf("%ld",ans);
        return 0;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值