序:除了剛開始的看了幾道題的題解,後來也自己肛出了幾道 剩下不可做的題不也沒做嗎
這些題目最大的特點是在於需要自己構造狀態,這往往會成為一道題的最大卡點 窮舉表示水不到幾分
題目選講:
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 入門水題 每個節點有權值,子節點和父節點不能同時選,問最後能選的最...