這道題思維難度非常高,有很多處理的小技巧,並且**也有很多細節 ;
這道題是一種序列的區間操作,我們都知道,區間操作比較麻煩,所以我們要想辦法將區間操作轉換成單點修改;
這時,我們想到了差分,假如我們對乙個序列進行操作,這時乙個序列裡的相對狀態不會變只有兩端改變,換句話說就是這個序列的差分並不會發生改變,只有兩端的差分陣列會發生變化。但這裡的差分並不是差
而是異或;對乙個區間[l,r]進行修改操作就是將差分陣列c[l],c[r+1]取反,最後我們的目標就是要將差分陣列全部變成0;
但這裡就有乙個細節:差分陣列全部變成0的意思就是所有的狀態都是一樣的,但有可能全部亮或者全不亮;這裡我們要假設第0個燈泡是亮的;然後差分出來才是合法的全亮狀態;
還有乙個細節:差分陣列要開到n+1來保證至少有偶數個數的1,這樣才能消去 ;
所以我們就這樣完成了第一步,將區間修改轉化成單點修改;
處理出來差分陣列之後,我們就要將這個序列全部變為0 ;
有兩種情況;乙個0乙個1,這樣操作之後1的個數沒有變 ;
兩個都是1,這種情況就是減少兩個1
但大多數情況並不能一次操作就完成,因為只能操作m種長度 ;
所以實際情況是1先和一些0交換,然後兩個1一起消去 ;
所以對所有1跑一邊bfs處理出到與其他1消去所需要的步數;
bfso(n)就可以完成;
處理完每個1與其他1消去之後,就要考慮如何選擇使其代價最少 ;
我們可以注意到其實原來的1很少不超過16個,於是我們可以開始考慮狀壓dp ;
我們反著dp;
dp[i]表示狀態為i到狀態為0所需要的最小步數 ;
初始化dp[0]=0 ;
然後倒著列舉點亮哪兩個燈,(因為倒著來是點亮,正著來就是消去兩個燈)
然後點亮完了的狀態就是i|(1最後再輸出dp[最初的狀態] ;
到此,這道題就順利的解決了;
具體細節請看**:
#include
using
namespace std;
const
int maxn =
40101
;int n,k,m ;
int a[maxn]
,c[maxn]
,dis[20]
[maxn]
;int len[maxn]
,sta[maxn]
,top=0;
int dp[
1<<17]
;bool vis[maxn]
;inline
void
read
(int
&x)while
(s>=
'0'&&s<=
'9')
x*=f ;
}void
bfs(
int s,
int*dis)
y=x+len[i];if
(y<=n+1&&
!vis[y])}
}}intgetdis
(int x,
int y)
intmain()
for(
int i=
1;i<=n+1;
++i) c[i]
=a[i]
^a[i-1]
;for
(int i=
1;i<=m;
++i)
read
(len[i]);
memset
(dis,
0x3f
,sizeof
(dis));
memset
(dp,
0x3f
,sizeof
(dp));
for(
int i=
1;i<=n+1;
++i)
if(c[i]
) sta[
++top]
=i,bfs
(i,dis[top]);
dp[0]
=0;for
(int i=
0,t=(1
<
;i++i)
for(
int j=fir+
1;j++j)}}
printf
("%d"
,dp[(1
<
]);return0;
}
星空 差分,狀壓dp
總算不再是能用暴力卡常 隨機化水過的好t3了。說是打了兩個標籤,實際上最關鍵的是題意轉化。如果你絲毫不轉化的話也可以 1 include2 using namespace std 3int dp 2 1048577 b 65 k,n,m,x 9 f 1 mx 4int main 15 printf ...
狀壓dp題解
實現 includeconst int maxn 1 20 1 typedef long long ll ll f maxn a 25 a x 記錄第x行的障礙狀態 int lowbit int x int main f 0 1 乙個也不放也是一種方案 int maxs 1 判斷當前狀態下狀態的1的...
題解 Vjestica (狀壓DP)
有n個字元合集,求其字首樹的最少結點個數。資料範圍 n 16,1 m 1000000 字元個數 首先看到 n 16 可知,這道題是狀壓dp或者暴力 狀態是什麼?因為是n的資料範圍小,n為字元合集個數,所以狀態是 n個字元合集的整體 的選擇情況,這裡用s表示,二進位制範圍為 1 11111111111...