acm模版
首先,我們先來分析一下如何構造才合法。
先預處理出來每種大小的數的個數,並在這個過程進行判斷是否連續(不大於
1 ),然後,我們可以從小到大進行插空法插數,那麼如何插呢?假如,此時我們已經查到數
i,那麼合法的插孔分為兩種,第一種是插在兩個 i−
1 之間,另一種就是當首尾有 i−
1 時,我們可以在首尾兩側進行插空。那麼我們需要考慮的也就是此時考慮到了第
i 種數、有多少個相鄰的第
i種數對兒、首尾有幾個第
i 種數,自然最後這個狀態只有三種,就是 0、
1、2,所以我們設 dp
[i][
j][0
/1/2
] 表示此時考慮到了第
i 種數時,首尾第
i種數的個數分別為 0、
1、2 ,相鄰的第
i 種數對兒個數加上首尾第
i種數的個數的和為
j 時的方案數。初始化,我們應該設 dp
[1][
cnt[
1]+1
][2]
=1,至於為什麼,很容易理解,此時,第一種數的方案只有一種,首尾第一種數的個數為
2 ,連續的第一種數對兒為 cn
t[1]
−1,所以,你沒有看錯,cn
t[1]
+1實際上就是此時插空時允許插空的數目。
綜上所述,這個題核心就是插空法,組合數學的知識,另外還要使用一次插空法的地方是,要考慮將 cn
t[i]
個數劃分為
k 份時。
哦,對了,最後結果自然是不同插空數時三種情況的和的和嘍!也就是說需要控制該 dp
的第二維的量,因為我們說明了,第二維實際上就是此時允許的插空數目。over~~~
#include
#include
#include
#include
using
namespace
std;
typedef
long
long ll;
const
int maxn = 3e4 + 5;
const
int maxm = 111;
const
int mod = 1e9 + 7;
int n;
int a[maxn];
int cnt[maxn];
ll c[maxm][maxm];
ll dp[maxn][maxm][3];
template
inline
bool scan_d(t &ret)
while (c != '-' && (c < '0' || c > '9'))
sgn = (c == '-') ? -1 : 1;
ret = (c == '-') ? 0 : (c - '0');
while (c = getchar(), c >= '0' && c <= '9')
ret *= sgn;
return1;}
void init()
}}int main()
if (a[i] - a[i - 1] > 1)
if (a[i] - a[i - 1] == 1)
else
}dp[1][cnt[1] + 1][2] = 1;
for (int i = 1; i < tot; i++)
if (x - k + 1 >= 0)
if (k - 2 >= 0 && x - k + 2 >= 0)}}
}ll ans = 0;
for (int i = 0; i <= cnt[tot] + 1; i++)
cout
<< ans << endl;
return
0;}
51Nod1623 完美消除
link solution 首先我們可以發現乙個結論,對於乙個數 x 它的最低修改次數為它每位與前去中是否都比此位上的數大,有則答案 1 因為若有小數則沒有辦法將其答案貢獻變低。這個東西可以直接單調棧維護乙個遞增序列。所以這樣 dp 狀態也很顯然了,設 f 表示當前到 i 位,答案為 j sta 表...
51Nod1367 完美森林 貪心
有一棵n個點的樹,樹中節點標號依次為0,1,2,n 1,其中n 500000。樹中有n 1條邊,這些邊長度不一定相同。現在要把樹中一些邊刪除,設刪除了k條邊 k 0,即可以不刪除任何邊 由樹的性質可知,該樹將被分割為乙個含有k 1棵樹的森林。稱乙個森林是 完美森林 要求這個森林中的每一棵樹滿足 該樹...
51nod 序列分解(dfs)
1400 序列分解 基準時間限制 1 秒 空間限制 131072 kb 分值 40 難度 4級演算法題 小刀和大刀是雙胞胎兄弟。今天他們玩乙個有意思的遊戲。大刀給小刀準備了乙個長度為n的整數序列。小刀試著把這個序列分解成兩個長度為n 2的子串行。這兩個子串行必須滿足以下兩個條件 1.他們不能相互重疊...