計數dp小結

2021-08-10 04:11:10 字數 3888 閱讀 5885

序:除了剛開始的看了幾道題的題解,後來也自己肛出了幾道 剩下不可做的題不也沒做嗎

這些題目最大的特點是在於需要自己構造狀態,這往往會成為一道題的最大卡點 窮舉表示水不到幾分

題目選講:

e:

如果直接模擬,複雜度為k∗

n2既然每一步只能往上下或往左右走,

那麼我們可以把題目分解為在x軸上行走k步與在y軸上行走k步的方案數都處理出來 k∗

n 然後列舉往左右走i步,往上下走k-i步,然後再用組合處理(常規套路)

#include

#include

#define p 9999991

int dp[2][1005][1005];

long long sum[2][1005];

int c[1005][1005];

int main()

}for(int cas=1;cas<=t;cas++)

}for(int j=1;j<=k;j++)

}long long ans=0;

for(int i=0;i<=k;i++)

printf("case #%d:\n%lld\n",cas,ans);

}return

0;}

f:

又是一道 關於位運算的題目,一看到資料範圍,就可以揣測出大概複雜度

求兩個序列,正著掃一遍,倒著掃一遍。然後列舉兩點(左序列的右端點,右序列的左端點),進行運算,這樣複雜度為o(

n3) 加乙個字首和優化就是o(

n2) 了

#include

#include

#define p 1000000007

const

int m=1023;

int a[1005];

long

long dp[2][1005][m+5],dp1[2][1005][m+5];

long

long sum[1005][m+5];

int main()

for(int j=0;j<=m;j++)

}dp1[0][n+1][m]=1;

for(int i=n;i>=1;i--)

for(int j=0;j<=m;j++)

}for(int i=1;i<=n;i++)

}long

long ans=0;

for(int j=0;j<=m;j++)

}printf("%lld\n",ans);

}return

0;}

g:

像這種兩維的題目可以存一維,列舉一維

定義dp兩維,一維是列舉到哪一列,另一維是有幾行是空著的

對於不同的情況,進行填充空行,或者是填充填過的行

#include

using namespace std;

const int m=55,p=1000000007;

long long dp[55][55],c[m][m],pow[m];

void init()

}pow[0]=1;

for(int i=1;i<=50;i++)pow[i]=pow[i-1]*2

%p;}

int main()}}

printf("%lld\n",dp[n][m]);

}return

0;}

h:

這道題的難度相比於上面的題目略大

在樹形dp的套路上還要加上組合數

雖然說是1-n的序列,但我們只用處理他們之間的大小關係就好了

可以證明:任意大小關係都可被不同的序列表示

在合併兩個子樹的時候,我們已知子樹自身節點的大小關係,但並不知道兩棵樹之間的大小關係,且這個關係是確定的,那麼就可以乘上c(

size

(a)s

ize(

a)+s

ize(

b))

合併完子樹之後,再加入根節點,根節點作為最大值時dp

[i]+

=dp[

i−1]

不然就是dp

[i]+

=dp[

i]∗s

ize(

a)。 si

ze(a

) 為以某節點根的樹的的節點個數

#include

#include

#include

using

namespace

std;

#define p 1000000007

vector

edge[1005];

long

long dp[1005][1005];//i節點,j個最大值

long

long dp1[1005][1005];

int c[1005][1005];

int degree[1005],n;

void dfs(int x,int f)

}for(int i=1;i<=n;i++)

}cnt+=degree[y];

flag=1;

}degree[x]=cnt+1;

if(!flag)dp[x][1]=1;

else

}}void init()

}}int main()

dfs(1,0);

printf("case #%d: %lld\n",++t,dp[1][k]);

}return

0;}

k:若直接根據題意模擬取了幾個數,然後用組合數來計算,複雜度為n3

然後思考優化,對於滿足條件的一組數,保證至少取兩個數,且不去兩個數就好了,那麼dp只用存 選到哪個數 累和為多少 不選幾個數 選幾個數

由於不選2個數和不選3.4….n個數的效果都是一樣的,所以只用存0-2就好了

同理與選幾個數

#include

#include

#define p 1000000007

int dp[1005][1005][3][3];//選到那個數 得到的值 可以不選 必須選

int a[1005];

void add(int &x,int y)

int main()

if(j+a[i]<=s)}}

}int ans=0;

for(int i=1;i<=s;i++)add(ans,dp[n][i][2][2]);

printf("%lld\n",1ll*ans*4%p);

}return

0;}

m:

是一道裸的遞推題

可以預處理出用j種顏色染i個格仔的方案數(第二類斯特林數)

然後列舉第一行用幾種顏色,第二行用幾種顏色

同時對於染色的方案再乘上顏色數量的全排

然後就可以在o(

n2) 的時間內算出答案

#include

#define m 2005

#define p 1000000007

long long dp[m][m],c[m][m],a[m];

void init()

}for(int i=1;i<=2000;i++)

}a[0]=1;

for(int i=1;i<=2000;i++)

}int main()

}printf("%lld\n",ans);

}return

0;}

計數DP 種樹

題目描述 事實上,小x邀請兩位奆老來的目的遠不止是玩鬥地主,主要是為了抓來苦力,替他的後花園種樹 小x的後花園是環形的,他想在花園周圍均勻地種上n棵樹,但是奆老花園的土壤當然非同尋常,每個位置適合種的樹都不一樣,一些樹可能會因為不適合這個位置的土壤而損失觀賞價值。小x最喜歡3種樹,這3種樹的高度分別...

python 計數方法小結

在專案中經常會遇見需要計數的情況,最近在看 利用python進行資料分析 這裡面提到了三個計數方法 方法一 遍曆法 def get counts sequence counts for x in sequence if x in counts counts x 1 else counts x 1 r...

樹形dp小結

這些天做了一些樹形dp的題目,感覺有了些領悟,尤其是理解到樹形揹包就是分組揹包之後。選出幾道不錯的總結一下 hdu 1520 hdu 4003 poj 1155 poj 2486 hdu 4313 hdu 4340 hdu 1520 入門水題 每個節點有權值,子節點和父節點不能同時選,問最後能選的最...