• cf gym102059G. Fascination Street(四维dp)


    题目大意:

      一条线上共$n$个点,$(n leq 250000)$每个都有一盏灯,每盏灯打开有一个费用$w_i(0 leq w_i leq 10^{9})$,并照亮自己以及左右共三个点。现在你可以交换任意两盏灯,最多进行$k$次交换$(k leq 9)$,求交换后,照亮所有点的最小花费。

    解题思路:

      一个显而易见的结论就是交换的两盏灯一定是一盏打开一盏没打开,否则交换没有任何意义。

      考虑采用$dp$,令$dp[i][j][k][l]$把表示前i盏灯,欠了$j$盏灯没开(需要换$j$盏灯过去),还了$k$盏灯($k$盏灯打开了但是要换到别的地方去),当前状态为$l$的最小花费,其中$l=0$表示i和i-1都没开,这时需要$i+1$打开,$l=1$表示$i$没打开但$i-1$打开了,这时候$i+1$即可以打开也可以不打开,$i=2$表示$i$打开了,$i+1$状态同$l=1$。需要注意的是如果$i$打开了,那么$i-1$打不打开其实无所谓,因为前者影响不到后面,所以二者都打开状态不需要维护,这里如果维护l就有四种状态,就会正好爆内存MLE了,当然dp的第一维可以用滚动数组压缩掉。然后考虑转移方程:

      先不考虑该点欠还的情况,很明显,对于$l=0$,$i$和$i-1$都不打开,明显可以只能从$i-1$打开而$i-2$打开得到,否则$i-1$就无法被照亮了,因此有$dp[i][j][k][0]=dp[i-1][j][k][1]$,对于$l=1$,转移也很简单,$i-1$要打开,所以$dp[i][j][k][1]=dp[i-1][j][k][2]$,而当l=2时,由于$i$要打开,所以$i-1$状态不用考虑,就有转移方程$dp[i][j][k][2]=min(dp[i-1][j][k][0],dp[i-1][j][k][1],dp[i-1][j][k][2])+w_i)$。

      然后考虑该点欠的情况,即这盏灯不点亮,然后换一盏点亮了的灯到这个点,所以这个点最终必须被点亮,也就是只有$l=2$状态,至于让谁换我们不在意,只需要知道这个点最终被点亮即可,于是有转移方程$dp[i][j][k][2]=min(dp[i-1][j-1][k][0],dp[i-1][j-1][k][1],dp[i-1][j-1][k][2]))$,由于这盏灯没有被点亮,先不计算点亮这个点的花费,等别的点换的时候在计算。

      最后考虑该点还给别人情况,即点亮它,然后换到别的地方,在换一盏不亮的到该点,所以换给别人的话,该点一定没有被点亮,所以有$dp[i][j][k][0]=dp[i-1][j][k-1][1]+w_i$,$dp[i][j][k][1]=dp[i-1][j][k-1][2]+w_i$,答案只需要对所有$dp[n][j][j][1]$和$dp[n][j][j][2]$取$min$即可,其中$0 leq j leq k$

      最后贴上AC代码,第一发MLE了,后面改了滚动数组。

    #include<bits/stdc++.h>
     
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair <ll,ll> pii;
    #define rep(i,x,y) for(int i=x;i<y;i++)
    #define rept(i,x,y) for(int i=x;i<=y;i++)
    #define per(i,x,y) for(int i=x;i>=y;i--)
    #define all(x) x.begin(),x.end()
    #define pb push_back
    #define fi first
    #define se second
    #define mes(a,b) memset(a,b,sizeof a)
    #define mp make_pair
    #define dd(x) cout<<#x<<"="<<x<<" "
    #define de(x) cout<<#x<<"="<<x<<"
    "
    #define debug() cout<<"I love Miyamizu Mitsuha forever.
    "
    const ll inf=1e18;
    const int maxn=250010;
     
    ll dp[4][11][11][3];
    int cost[maxn];
     
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        int n,m;
        cin>>n>>m;
        rept(i,1,n) cin>>cost[i];
        rept(i,0,1) rept(j,0,m) rept(k,0,m) rept(l,0,2) dp[i][j][k][l]=inf;
        dp[0][0][0][0]=dp[0][0][0][1]=0;
        rept(i,1,n)
        {
            rept(j,0,m)
            {
                rept(k,0,m)
                {
                    rept(l,0,2) dp[i&1][j][k][l]=inf;
                    dp[i&1][j][k][0]=min(dp[i&1][j][k][0],dp[(i-1)&1][j][k][1]);
                    dp[i&1][j][k][1]=min(dp[i&1][j][k][1],dp[(i-1)&1][j][k][2]);
                    dp[i&1][j][k][2]=min(dp[i&1][j][k][2],min(dp[(i-1)&1][j][k][0],min(dp[(i-1)&1][j][k][1],dp[(i-1)&1][j][k][2]))+cost[i]);
                    if(j)//owe
                    {
                        dp[i&1][j][k][2]=min(dp[i&1][j][k][2],min(dp[(i-1)&1][j-1][k][0],min(dp[(i-1)&1][j-1][k][1],dp[(i-1)&1][j-1][k][2])));
                    }
                    if(k)//pay bcak
                    {
                        dp[i&1][j][k][0]=min(dp[i&1][j][k][0],dp[(i-1)&1][j][k-1][1]+cost[i]);
                        dp[i&1][j][k][1]=min(dp[i&1][j][k][1],dp[(i-1)&1][j][k-1][2]+cost[i]);
                    }
                }
            }
        }
        ll ans=inf;
        rept(j,0,m)
        {
            ans=min(ans,min(dp[n&1][j][j][1],dp[n&1][j][j][2]));
        }
        cout<<ans<<"
    ";
        return 0;
    }
  • 相关阅读:
    胖虎都看得懂的CSS入门
    Python-ORM之sqlalchemy的简单使用
    类似fabric主机管理demo
    Redis 数据库学习
    sublime 3插件安装记录
    斐波那契数列—java实现
    mysql基础操作记录
    [转]修改github已提交的用户名和邮箱
    python nose的html报告优化
    python report中文显示乱码
  • 原文地址:https://www.cnblogs.com/FZUzyz/p/13143931.html
Copyright © 2020-2023  润新知