【遞推DP+加深】
zoj 3747
題意:給n個士兵排隊,每個士兵三種G、R、P可選,求至少有m個連續G士兵,最多有k個連續R士兵的排列的種數。
都轉化為至多的士兵連續的個數。
令集合A={至多n個G士兵連續,且至多K個R士兵連續}
集合B={至多m-1個G士兵連續,且至多K個連續的R士兵連續}
C=A-B={至少m個士兵連續,且至少連續K個士兵連續}。
在轉化要如何求 至多x個G士兵連續,至多y個士兵連續
dp[i][0]至多i個G 的方案數
dp[i][1]至多i個R 的方案數
dp[i][2]第i個為P 的方案數
對G士兵遞推:
當i<=x時
dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];
當i=x+1時候
dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1; 減去的是x+1位置為G士兵的時候
當i>x+1的時候
dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-x-1][1]-dp[i-x-1][2]; 此時要減去i前面已經出現連續u個G的情況,即從i-u到i-1這一段都是G,
那麼i-1-u的位置可以是P或者R,
對士兵R遞推:
同理
#include <bits/stdc++.h> #include <iostream> #define X 10005 #define inf 0x3f3f3f3f #define PI 3.141592653589793238462643383 #define IO ios::sync_with_stdio(false),cin.tie(0), cout.tie(0); #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; typedef long long ll; const ll mod=1e9+7; const int maxn=1e6+10; ll dp[maxn][4]; int n,m,k; ll solve(int x,int y)//dp[i][0]至多i個G dp[i][1]至多i個R dp[i][2]第i個為P { dp[0][0]=1; dp[0][1]=dp[0][2]=0; for(int i=1;i<=n;++i) { ll ans=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod; dp[i][2]=ans; if(i<=x)dp[i][0]=ans; if(i==x+1)dp[i][0]=(ans-1+mod)%mod; if(i>x+1)dp[i][0]=(ans-dp[i-x-1][1]-dp[i-x-1][2]+mod)%mod; if(i<=y)dp[i][1]=ans; if(i==y+1)dp[i][1]=(ans-1+mod)%mod; if(i>y+1)dp[i][1]=(ans-dp[i-x-1][0]-dp[i-x-1][2]+mod)%mod; } return (dp[n][0]+dp[n][1]+dp[n][2])%mod; } int main() { while(cin>>n>>m>>k) { cout<<((solve(n,k)-solve(m-1,k))%mod+mod)%mod<<endl; } return 0; }
uva10328 Coin Toss(計數問題)
就是拋硬幣正面為H,反面為T,問拋n次的所有可能情況中連續H的個數至少為k的個數。
比起那個zoj3738這道題算是比較好想了,不過是同一種類型的題目,
dp[i][0]表示前i個正面朝上的方案總數,且第i個為正面朝上,且朝上的個數不超過k個
dp[i][1]為第i個位反面朝上,且前i個朝上的個數不超過k個
那麼題目就求出最多n個朝上的總方案數-最多k-1個正面朝上的總方案數
不過和他的兄弟題目zoj 3738 在 dp[0][0]=1或者=0的時候我有點迷
另一個思路:連結
設f[i][j]表示拋第i個硬幣時連續的H不超過j個的情況總數,那麼由於第i個可以為正可以為負,因此不妨先寫成f[i][j]=2*f[i-1][j]。
但是這麼寫有些情況下是不正確的,什麼情況呢?就是前面i-1個硬幣的最後j個全是H,那麼第i個如果再是H的話,總長度就變成j+1了。
在減去這部分的種數的時候,我們要分情況討論一下。如果i==j+1,那麼就只有一種情況可以出現j+1個H,就是i個硬幣全部是H,因此減去1即可。如果i>j+1,那麼如果第i個硬幣前面有j個H的話,那麼第i-j-1個硬幣必然應該是T,否則H的連續的數量就會超過j,這種情況就不可能包含在f[i-1][j]裡面了,
因此,我們這時只要減去一個f[i-j-2][j]即可。
最後結果顯然應該是輸出f[n][n]-f[n][k-1]。
#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
ll dp[maxn][2];
ll solve(int n,int k)
{
memset(dp,0,sizeof(dp));
dp[0][0]=0,dp[0][1]=1;
for(int i=1;i<=n;++i)
{
ll ans=dp[i-1][0]+dp[i-1][1];
dp[i][1]=ans;
if(i<=k)dp[i][0]=ans;
if(i==k+1) dp[i][0]=ans-1;
if(i>k) dp[i][0]=ans-dp[i-k-1][1];
}
return dp[n][0]+dp[n][1];
}
int main()
{
int n,k;
while(cin>>n>>k)
{
cout<<solve(n,n)<<endl;
cout<<solve(n,n)-solve(n,k-1)<<endl;
}
return 0;
}
HDU 4489
求n個人按照低高低或者高低高的順序排列種數是多少
The king has guards of all different heights. Rather than line them up in increasing or decreasing height order, he wants to line them up so each guard is either shorter than the guards next to him or taller than the guards next to him (so the heights go up and down along the line). For example, seven guards of heights 160, 162, 164, 166, 168, 170 and 172 cm. could be arranged as:
or perhaps:
The king wants to know how many guards he needs so he can have a different up and down order at each changing of the guard for rest of his reign. To be able to do this, he needs to know for a given number of guards, n, how many different up and down orders there are:
For example, if there are four guards: 1, 2, 3,4 can be arrange as:
1324, 2143, 3142, 2314, 3412, 4231, 4132, 2413, 3241, 1423
For this problem, you will write a program that takes as input a positive integer n, the number of guards and returns the number of up and down orders for n guards of differing heights.
Input
The first line of input contains a single integer P, (1 <= P <= 1000), which is the number of data sets that follow. Each data set consists of single line of input containing two integers. The first integer, D is the data set number. The second integer, n (1 <= n <= 20), is the number of guards of differing heights.
Output
For each data set there is one line of output. It contains the data set number (D) followed by a single space, followed by the number of up and down orders for the n guards.
Sample Input
4 1 1 2 3 3 4 4 20
Sample Output
1 1 2 4 3 10 4 740742376475050
參考:連結
題意:求n個人按照低高低或者高低高的順序排列種數是多少
把n個人的身高設為1~n, 然後從低到高插入佇列。 那麼將第i個人插入佇列的時候就出現了問題, 插入的這個位置需要滿足前面兩個是高低, 後面兩個是低高。
所以我們用DP來記錄。 用d[i][0] 表示i個人的佇列, 結尾為高低的方法數, d[i][1]表示開頭為低高的方法數。 那麼假設將第i個人插入, 插入的位置前面有j個人, 後面有i - 1 - j個人, 那麼當前方法數就累加d[j][0] * d[i-1-j][1] * c[i-1][j]。 最終求得的是有i個人的時候的所有方法數,
那麼怎麼遞推DP陣列d[i][1] 和 d[i][0] 呢,因為開始為低高和結尾為高低的方法數相同且各佔總方法數的一半
證明:
當n為偶數時:假設高低開始的序列為1010.那麼把它倒置一下就變成了0101了
也就是說每一個1打頭的對應著一個0打頭的
當n為奇數時:假設波峰開始的序列為10101.
假設第一個數大於最後一個數,那我們把序列最後一個數放到最前序列就變成01010.
如果第一個數小於最後一個數把第一個數放到最後就行了
#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
ll dp[50][2];
ll c[50][50];
int pos,n;
void init()
{
for(int i=1;i<=20;++i)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;++j)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
dp[0][1]=dp[0][0]=1;
dp[1][0]=dp[1][1]=1;
ll cur=0;
for(int i=2;i<=20;++i)
{
cur=0;
for(int j=0;j<i;++j)
cur+=dp[j][0]*dp[i-j-1][1]*c[i-1][j];
dp[i][1]=dp[i][0]=cur/2;
}
}
int main()
{
init();
int t;
cin>>t;
while(t--)
{
cin>>pos>>n;
if(n==1){ cout<<pos<<' '<<1<<endl;continue;}
cout<<pos<<' '<<dp[n][0]+dp[n][1]<<endl;
}
return 0;
}