1. 程式人生 > >2017省夏令營Day6 【dp】

2017省夏令營Day6 【dp】

數列 .com ffffff div print display ring 成了 play

  • 技術分享

技術分享

題解:區間dp,f[i][j]表示區間[i,j]的狼全部消滅的最小代價,設k為i、j間任意一點(i<=k<=j),且第k只狼被最後消滅,顯然,區間總代價即可被我們劃分成[i,k-1]和[k+1,j]兩部分,我們可以假設他們已知,於是求得兩區間代價和再加上消滅第k只狼的代價就能求得區間[i,j]的總代價.

狀態轉移方程:f[i][j]=f[i][k-1]+f[k+1][i]+a[k]+b[i-1]+b[j+1]。

PS:註意初始化時i要從0開始枚舉,且若j<i時f值為0。

代碼如下:

 1 #include<cstdio>
 2 #include<iostream>
 3
#include<cstring> 4 #define INF 0x7fffffff 5 using namespace std; 6 int n,a[405],b[405],f[405][405],g[405][405]; 7 int main() 8 { 9 freopen("wolf.in","r",stdin); 10 freopen("wolf.out","w",stdout); 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 13 for
(int i=1;i<=n;i++) scanf("%d",&b[i]); 14 for(int i=0;i<=n;i++) 15 for(int j=i;j<=n;j++){ 16 if(i==j) f[i][j]=a[i]+b[j-1]+b[j+1]; 17 else f[i][j]=INF; 18 } 19 for(int l=1;l<n;l++) 20 for(int i=1;i+l<=n;i++){ 21 int j=i+l;
22 for(int k=i;k<=j;k++) 23 f[i][j]=min(f[i][j],f[i][k-1]+f[k+1][j]+a[k]+b[i-1]+b[j+1]); 24 } 25 printf("%d",f[1][n]); 26 return 0; 27 }

-------------------------------------------------華麗的分割線---------------------------------------------------------------

技術分享

題解:首先,我們預處理1-88的斐波那契數列 f,可以推出一條規律:如果只有第i個數為答案,答案數為(i+1)/2,如果不保留這個數(此位為0),答案為(i-1)/2(“例子見下“*”)

我們從大到小枚舉斐波那契數,如果f[i]比n小那麽就把i存入a數組並把n-f[i],a[i]表示第i個1所在斐波那契數列第幾項,於是得出一個像二進制的01串,我們就可以從低位向高位dp。

狀態轉移方程:①g[i][1]=g[i-1][0]+g[i-1][1](如果第i個1還為1,那麽加入這個點對答案無影響,直接轉移即可)

②g[i][0]=g[i-1][1]*(a[i]-a[i-1]-1)/2+g[i-1][0]*(a[i]-a[i-1])/2 (如果第i個1變成0,表明第i個1變成了前2項的和並繼續向前拓展,統計答案方法如上,但是要註意如果前一個1還為1統計答案時區間長度-1,因為前一個1的位置不能用於統計)

初始化:g[1][1]=1,g[1][0]=(a[1]-1)/2;

*:若斐波那契數列第5項為1,可以把第5項改為0,並把3、4兩項改為1,第4項無法再修改,可把第3項改為0,並把1、2項修改為1,總答案數為(5+1)/2=3;若不包含第5項(為0),答案數為(5-1)/2=2;

代碼如下:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 using namespace std;
 6 long long n,f[90],a[90],g[90][2];
 7 int main()
 8 {
 9     freopen("fibonacci.in","r",stdin);
10     freopen("fibonacci.out","w",stdout);
11     f[1]=1; f[2]=2;
12     for(int i=3;i<=88;i++) f[i]=f[i-1]+f[i-2];
13     int T; scanf("%d",&T);
14     while(T--){
15         scanf("%lld",&n);
16         int cnt=0;
17         for(int i=88;i>=1;i--)
18             if(f[i]<=n){n-=f[i],a[++cnt]=1LL*i;}
19         reverse(a+1,a+cnt+1);
20         g[1][1]=1;
21         g[1][0]=(a[1]-1)>>1;
22         for(int i=2;i<=cnt;i++){
23             g[i][1]=g[i-1][0]+g[i-1][1];
24             g[i][0]=((a[i]-a[i-1]-1)>>1)*g[i-1][1]+((a[i]-a[i-1])>>1)*g[i-1][0];
25         }
26         printf("%lld\n",g[cnt][0]+g[cnt][1]);
27     }
28     return 0;
29 }

2017省夏令營Day6 【dp】