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<<"\n"
#define debug() cout<<"I love Miyamizu Mitsuha forever.\n"
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<<"\n";
    return 0;
}

 

posted @ 2020-06-16 19:32  GGMU  阅读(160)  评论(0编辑  收藏  举报