题目大意:
一条线上共$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; }