URAL 1900. Brainwashing Device

乍看此题,还以为是扫描线+贪心,每次都扫描出最多的那个站,但是时间复杂度太高,而且写到一半的时候发现了反例. . . . . 


接下来开始推式子:


dp[ i ][ j ] = max(dp[ k1 ][ j ],dp[ k2 ][ j -1] + ans[site[ k2 ][j-1]+1][i])。

k1 ∈[j,i],k2 ∈[j-1,i].

site[ i ][ j ]表示的dp[ i ][ j ]这种情况下最右边的洗脑设备的位置.

ans[ i ][ j ]表示 以 k ∈ [ i , j ] 为始发站,经过 j 且不以 j 为终点的客人数量.


好了,理清上面的思路后问题的难点就变成了 如何统计出 ans[ ][ ]。

如果单纯的暴力枚举时间复杂度是o(n^4),二维的线段树倒是可以胜任,但是我忘了怎么写了。。。


然后学姐说可以试一试能否转化成简单的矩阵DP。


设,Map[ i ][ j ]存储的为 i 到 j 的客流量,i >= j 时,Map[ i ][ j ] = 0。

则ans[ x ][ y ] = sigma Map[ i ][ j ] ( x <= i <= y ,y < j <= n)。

接下来可以用 2*n^2 的时间将Map[ i ][ j ]处理成 (1,1) 到 ( i , j )的矩形范围内元素的和。

ans[ i ][ j ] = Map[ j ][ n ] - Map[ j ][ j ] - Map[ i-1 ][ n ] + Map[ i-1 ][ j ]。

显然这种方法预处理的时间较高,但查询只需要o(1)。缺点在于不能更新。


对于打印路径只需要在dp的时候记录一下前驱即可,不再赘述。


就这个烂题竟然卡了两天。。。。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <stack>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long int
#define ULL unsigned long long int
#define _LL __int64
#define _INF 0x3f3f3f3f
#define INF 40000000
#define Mod 1000000009

using namespace std;

int Map[501][501];

struct N
{
    int site,data,pn,pm;
}dp[501][501];

int ans[501][501];

bool path[510];

void dfs(int n,int m)
{
    if(n == 0 || m == 0)
        return ;
    if(dp[n][m].pm != m)
        path[n] = true;
    dfs(dp[n][m].pn,dp[n][m].pm);
}

int main()
{
    int n,m,i,j,k,t;

    scanf("%d %d",&n,&m);

    memset(Map,0,sizeof(Map));

    for(i = 1;i < n; ++i)
    {
        for(j = i+1;j <= n; ++j)
        {
            scanf("%d",&Map[i][j]);
        }
    }

    for(i = 1;i <= n; ++i)
    {
        for(j = 1;j <= n; ++j)
        {
            Map[i][j] += Map[i][j-1];
        }
    }

    for(j = 1;j <= n; ++j)
    {
        for(i = 1;i <= n; ++i)
        {
            Map[i][j] += Map[i-1][j];
        }
    }

    memset(ans,0,sizeof(ans));

    for(i = 1;i <= n; ++i)
    {
        for(j = 1;j <= n; ++j)
        {
            ans[i][j] = Map[j][n] - Map[j][j] - Map[i-1][n] + Map[i-1][j];
        }
    }

    for(i = 0;i <= n; ++i)
    {
        dp[i][0].data = 0;
        dp[i][0].site = 0;
    }

    for(i = 1;i <= n; ++i)
    {
        for(t = min(i,m),j = 1;j <= t; ++j)
        {
            dp[i][j].data = dp[j-1][j-1].data + ans[dp[j-1][j-1].site+1][i];
            dp[i][j].site = i;
            dp[i][j].pn = j-1;
            dp[i][j].pm = j-1;

            for(k = j;k <= i; ++k)
            {
                if(dp[k][j].data > dp[k][j-1].data + ans[dp[k][j-1].site+1][i])
                {
                    if(dp[k][j].data > dp[i][j].data)
                    {
                        dp[i][j] = dp[k][j];
                        dp[i][j].pn = k;
                        dp[i][j].pm = j;
                    }
                }
                else
                {
                    if(dp[k][j-1].data + ans[dp[k][j-1].site+1][i] > dp[i][j].data)
                    {
                        dp[i][j].data = dp[k][j-1].data + ans[dp[k][j-1].site+1][i];
                        dp[i][j].site = i;
                        dp[i][j].pn = k;
                        dp[i][j].pm = j-1;
                    }
                }
            }
        }
    }

    memset(path,false,sizeof(path));

    dfs(n-1,m);

    printf("%d\n",dp[n][m].data);

    for(i = 1;i <= n;++i)
    {
        if(path[i])
        {
            printf("%d",i);
            break;
        }
    }

    for(++i;i <= n;++i)
    {
        if(path[i])
        {
            printf(" %d",i);
        }
    }

    puts("");

    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值