10-19 dp专练

dp专练,终于克服了一次自己对dp的恐惧,磕出来一道题。

得分情况:

T1:0

T2:0

T3:0

emmmm,磕出来的题是T2,但是因为初始化和int long long的原因爆零了

T1:n只狼排成一排,每个狼的攻击力分a,b两种,当消灭第i只狼时,需要付出的代价是ai+左右两只狼的b攻击力,消灭i后第i+1和i-1只狼会重新排在一起(如果有的话),求消灭n指只狼的最小代价

很简单的一道区间dp,但是当时我想这个消灭的顺序不一样代价也不一样,所以没想区间dp,直接开始想T2了。

正解:dp[i][j]表示消灭i~j的最小代价,有状态转移方程dp[i][j]=b[i-1]+b[j+1]+min{dp[i][k-1]+dp[k+1][j]};

最后在加上所有的a就行了

T2:求一个正整数N用斐波那契数列中的数不重复表示的方案数。

首先我们要把n分解为斐波拉契数的和,这里用贪心最大分解即可。保存的是斐波拉契数列的序列号。 比如9就分解为1,8序列号为1,5 然后我们注意到其实序列号n,n+1和n+2的分解是一样的,用二进制表示就是110和

001是一样的。 接着我们可以看到,比如1000000这里表示的是数字21贪心后用二进制表示的分解方案,那么他不同的分解方案就有 0110000 0101100 0101011 这里我们看到有三种方案,可以看出在100变成011的方

案的时候,能够使得起始1后面的0都能有变成1的情况,即被选中的情况。 题意要求是用不同的斐波拉契数组合相加,那么如果n的方案为100000000010000000....000001000000这样的时候,随着第一个1的分解,最

终会覆盖到下一个1,这样就不符合题意了切重复统计了。 这样也给dp算法带来了条件。 令dp[i][1]为第i个斐波拉契数列被选中的情况 dp[i][0]则是未被选中。 可知dp[i][1]=dp[i-1][0]+dp[i-1][1]; 那么 dp[i][0]就要分两种情

况了 如果i-1还被选中,那么能给1一直下放为011的空间就只有INDEX[i]-INDEX[i-1]-1的空间了,不然覆盖到了i-1就是重复统计了,如果i-1不被选中就是p[i]-p[i-1]的空间了。 可以知道有多少连续的0的空间k就

有k/2种分解方案。 这样就可以写出方程

dp[i][1]=dp[i-1][0]+dp[i-1][1];

dp[i][0]=(p[i]-p[i-1])/2*dp[i-1][0]+(p[i]-p[i-1]-1)/2*dp[i-1][1];

最后!多组数据一定要初始化!!!数据范围大的记得用long long!!!!!!

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<ctime>
#define LL long long
using namespace std;

LL fe[100],p[100],f[500][2];
LL m=0,T,n;

int main(){
    freopen("fibonacci.in","r",stdin);
    freopen("fibonacci.out","w",stdout);
    
    cin>>T;
    while(T --> 0){
        memset(f,0,sizeof(f));
        memset(p,0,sizeof(p));
        memset(fe,0,sizeof(fe));
        scanf("%lld",&n);  
        fe[1]=1; fe[2]=2;   
        for(int i=3;;i++){
                fe[i]=fe[i-1]+fe[i-2];
            if(fe[i]>=n){
                m=i;break;
            }
        }  
        for(int i=m;i>0;i--){
            if(n>=fe[i]){
                n-=fe[i];
                p[++p[0]]=i;
            }
        }
        sort(p+1,p+p[0]+1);  
        f[1][1]=1;f[1][0]=(p[1]-1)/2;
        for(int i=2;i<=p[0];i++){  
            f[i][1]=f[i-1][0]+f[i-1][1];  
            f[i][0]=f[i-1][1]*((p[i]-p[i-1]-1)/2)+f[i-1][0]*((p[i]-p[i-1])/2);
        }  
        printf("%lld\n",f[p[0]][0]+f[p[0]][1]);
    }
    
    fclose(stdin);fclose(stdout);
    return 0;
}

T3:给定n重循环,每重循环的循环变量为前n个小写字母,给定每重循环的上界和下界,其可能是一个数字,也可能是一个循环变量,但是上界和下界只有可能出现一个循环变量,循环最里面写有cnt++,刚开始cnt=0求cnt最后的值

每重循环的上下界至多出现一个之前的字母,所以我们可以考虑把每个字母连向它这层循环上下界所出现的字母(如果上下界没有字母,就连向一个0号节点)。这样形成的就是一个树结构,并且子树之间的循环是独立的,可以用乘法把它们之间的循环次数连接起来。 设g(u,x)表示字母u取值为x时,u的子树的循环次数。再设f(u,x)为g(u,x)的前缀和。求g(u,x)时,考虑u取值为𝗶x时u的某个儿子的循环上下界,用前缀和求出循环次数。不同儿子之间的循环次数应该被乘起来。

完美,爆零滚粗。

posted @ 2017-10-19 16:23  assassin灬  阅读(233)  评论(0编辑  收藏  举报