[CC PARSIN]math

本文介绍了一种利用三角恒等变换优化动态规划(DP)问题的方法,并通过矩阵乘法进一步加速计算过程。文章详细展示了如何通过递推公式简化问题,并给出了具体的C++实现代码。

题目描述

这里写图片描述

预备知识

sin(a+b)=sin(a)cos(b)+cos(a)sin(b)

cos(a+b)=cos(a)cos(b)sin(a)sin(b)

cos(2a)=cos2(a)sin2(a)=2cos2(a)1

sin(2a)=2sin(a)cos(a)

cos2(a)+sin2(a)=1

DP

首先设F(N,M)表示M个数相加为N的答案。
F(N,M)=Mk=1F(NK,M1)sin(KX)
后面那个东西是什么?

推导

首先
sin(KX)=sin((K1)X+X)=sin((K1)X)cos(X)+cos((K1)X)sin(X)
sin(KX)=sin((K2)X+2X)=sin((K2)X)cos(2X)+cos((K2)X)sin(2X)
接下来把两式相加并除以2即可,相加结果
sin((K1)X+X)=sin((K1)X)cos(X)+cos((K1)X)sin(X)+sin((K2)X)cos(2X)+cos((K2)X)sin(2X)
对括号内是2X的使用倍角公式,变成下式
2cos2(x)sin((K2)X)+2sin(X)cos(X)cos((K2)X)sin((K2)X)+cos(X)sin((K1)X)+sin(X)cos((K1)X)
cos(X)sin((K1)X)变成cos(X)[sin((K2)X)cos(X)+cos((K2)X)sin(X)]
代入并合并同类项
3cos2(X)sin((K2)X)+3sin(X)cos(X)cos((K2)X)+sin(X)cos((K1)X)sin((K2)X)
sin(X)cos((K1)X)变成sin(X)[cos((K2)X)cos(X)sin((K2)X)sin(X)]
代入并合并同类项
3cos2(X)sin((K2)X)+4sin(X)cos(X)cos((K2)X)sin2(X)sin((K2)X)sin((K2)X)
sin2(X)sin((K2)X)变成(1cos2(X))sin((K2)X)
代入并合并同类项
4cos(X)[cos(X)sin((K2)X)+sin(X)cos((K2)X)]2sin((K2)X)
注意到[]里的就是和角公式,等于sin((K1)X)
然后得到结论
sin(KX)=2cos(X)sin((K1)X)sin((K2)X)
回到DP,只要讨论K是否为1就可以优化DP式变成
F(N,M)=F(N-1,M-1)+2cos(X)F(N-1,M)-F(N-2,M)
然后矩阵乘法即可

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef double db;
const int maxm=30+10;
struct dong{
    db a[65][65];
};
int s[30];
int i,j,k,l,t,n,m,ca;
db x,y,p;
dong a,b;
void mult(dong a,dong b,dong &c){
    dong d;
    int i,j,k;
    fo(i,1,60)
        fo(j,1,60)
            d.a[i][j]=0;
    fo(k,1,60)
        fo(i,1,60)
            if (a.a[i][k])
                fo(j,1,60)
                    if (b.a[k][j])
                        d.a[i][j]=d.a[i][j]+a.a[i][k]*b.a[k][j];
    fo(i,1,60)
        fo(j,1,60) 
            c.a[i][j]=d.a[i][j];
}
int main(){
    scanf("%d",&ca);
    while (ca--){
        scanf("%d%d",&m,&n);
        scanf("%lf",&x);
        fo(i,1,60)
            fo(j,1,60)
                a.a[i][j]=b.a[i][j]=0;
        fo(i,31,60){
            b.a[i][i-30]=1;
            b.a[i][i]=2*cos(x);
        }
        fo(i,1,30) b.a[i][30+i]=-1;
        fo(i,31,59) b.a[i][i+1]=sin(x);
        a.a[1][1]=sin(x);
        a.a[1][31]=sin(2*x);
        a.a[1][32]=sin(x)*sin(x);
        t=n-1;
        while (t){
            if (t%2) mult(a,b,a);
            mult(b,b,b);
            t/=2;
        }
        y=a.a[1][m];
        if (y>0) printf("+");else printf("-");
        y=fabs(y);
        p=1;
        if (y<1){
            while (1){
                if (y<p&&y>=p/10){
                    printf("%d\n",int(y/(p/10)));
                    break;
                }
                p/=10;
            }
        }
        else{
            while (1){
                if (y>=p&&y<p*10){
                    printf("%d\n",int(y/p));
                    break;
                }
                p*=10;
            }
        }
    }
}
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值