題目大意:你有乙個初始為
1 到
n的順序陣列
問題一:恰好進行
k 次相鄰交換,最後有多少不同的排列
問題二:進行不多於
k次交換(不一定相鄰),最後有多少種不同的排列 (n
,k<=
3000)
思路:這道題沒有什麼新意,其主要思路就是將恰好變為最少,即乙個排列我們要用最少的操作次數得到。
對於第一問,考慮乙個排列,我們要求出在最少交換次數的情況下如何得到它。 設f
[i][
j]為到第
i 個數,目前交換了
j次得到的排列個數,那麼每次轉移就是直接把新的數往前面的序列裡插,注意轉移優化。
考慮交換相鄰的數會導致總的逆序對的奇偶性發生反轉,假設乙個排列用
i 次得到,那麼如果i與
k 奇偶性相同,則此排列一定可以滿足恰好k次操作得到(我們可以無聊的交換
i次得到的排列的相鄰兩項)
否則一定不可以得到(根據逆序對的奇偶性)
這樣我們解決了第一問
第二問和第一問並沒有什麼區別,考慮這個可以兩兩交換,我們要在次數最少的情況下得到最終排列,從置換群的角度看最終的排列,構建有向圖,發現最終排列的本質是乙個個有向圈,很明顯可以證明乙個排列經過最少交換次數得到一定是從小到大插入有向圈裡
根據這個性質我們直接設f[
i][j
] 為前
i 個數使用了
j次交換,那麼每次相當於把乙個數插入到有向圈裡,直接轉移就好啦。
**:
#include
#include
#include
#include
#define n 3000
using
namespace
std;
typedef
long
long ll;
ll ans,n,k,p,f[n + 5][n + 5];
int main()
for (int j = 0;j <= k; ++j)
}for (int i = 1;i <= k; ++i) f[n][i] = (f[n][i] + f[n][i - 1]) % p;
for (int i = 0;i <= k; ++i)
if (!((k - i)&1))
ans = (ans + f[n][i]) % p;
if (ans < 0) ans += p;
cout
<" ";
memset(f,0,sizeof(f));
f[0][0] = 1;
for (int i = 1;i <= n; ++i)
ans = 0;
for (int i = 0;i <= k; ++i) ans = (ans + f[n][i]) % p;
cout
0;}
總結:1.當求一些操作序列多少次的時候可以轉化為在最少情況下得到,根據這個設計dp狀態 51nod 1250 排列與交換
對a進行好恰好k次相鄰交換,能得到多少個不同的序列 s1 對a進行最多k次交換,你能得到多少個不同的序列 s2 一次相鄰交換是指交換陣列a中兩個相鄰位置的元素,即 交換a i 和a i 1 或者a i 和a i 1 一次交換是指交換陣列a中的任意兩個位置不同的元素,即 交換a i 和a j 1 i,...
51Nod1250 排列與交換
乙個陣列a 1,2,3,n 對a進行好恰好k次相鄰交換,能得到多少個不同的序列 s1 對a進行最多k次交換,你能得到多少個不同的序列 s2 一次相鄰交換是指交換陣列a中兩個相鄰位置的元素,即 交換a i 和a i 1 或者a i 和a i 1 一次交換是指交換陣列a中的任意兩個位置不同的元素,即 交...
51nod 1574 排列轉換
現在有兩個長度為n的排列p和s。要求通過交換使得p變成s。交換 pi 和 pj 的代價是 i j 要求使用最少的代價讓p變成s。單組測試資料。第一行有乙個整數n 1 n 200000 表示排列的長度。第二行有n個範圍是1到n的整數,表示排列p。每個整數只出現一次。第三行有n個範圍是1到n的整數,表示...