Memory and Scores

本文解析了CodeForces D题“Memory and Scores”的解题思路,采用动态规划方法求解Memory与Lexa的得分差的方案数。通过两次优化,将时间复杂度从O(k^2t^2)降至O(kt^2),并提供了详细的代码实现。

Memory and Scores

题目链接:http://codeforces.com/contest/712/problem/D

dp

因为每轮Memory和Lexa能取的都在[-k,k],也就是说每轮两人分数的变化量在[-2k,2k];

故可以定义状态:dp[times][diff]为第times次Memory和Lexa的分数差为diff的方案数.

而dp[times][diff]可以从dp[times-1][diff-2k]到dp[times-1][diff+2k]转移而来;

又因为变化量为-2k时的方案数为1(-k,k),

变化量为-2k+1时的方案数为2(-k,k-1;-k+1,k),

变化量为-2k+2时的方案数为3(-k,k-2;-k+1,k-1;-k+2,k),

...,

变化量为-2k+m时的方案数为m+1,

...,

变化量为0时的方案数为2k+1,

...,

变化量为2k-m时的方案数为m+1,

...,

变化量为2k-1时的方案数为2,

变化量为2k时的方案数为1.

所以状态转移方程为:dp[times][diff]=dp[times-1][diff-2k]+2*dp[times-1][diff-2k+1]+3*dp[times-1][diff-2k+2]+...+(m+1)*dp[times-1][diff-2k+m]+...+2*dp[times-1][diff+2k-1]+dp[times-1][diff+2k];

这样的话,时间复杂度为O(k2t2),代码如下:

 1 #include<iostream>
 2 #include<cmath>
 3 #define M 1000000007LL
 4 #define TIME 105
 5 #define DIFF 300000
 6 #define BASE 150000
 7 using namespace std;
 8 typedef long long LL;
 9 LL a,b,k,t,ans;
10 LL dp[TIME][DIFF];
11 int main(void){
12     cin>>a>>b>>k>>t;
13     dp[0][a-b+BASE]=1;
14     LL upper=a-b+BASE+2*k*t;
15     LL lower=a-b+BASE-2*k*t;
16     for(LL times=1;times<=t;++times){
17         for(LL diff=lower;diff<=upper;diff++){
18             for(LL m=0;m<=2*k;m++){
19                 LL add=-2*k+m;
20                 if(diff+add>=lower){
21                     if(add)dp[times][diff]+=(dp[times-1][diff+add]+dp[times-1][diff-add])*(m+1);
22                     else dp[times][diff]+=dp[times-1][diff]*(m+1);
23                     dp[times][diff]%=M;
24                 }
25             }
26         }
27     }
28     for(int i=BASE+1;i<=upper;++i)
29         ans=(ans+dp[t][i])%M;
30     cout<<ans<<endl;
31 }
View Code

很显然,这会T,所以必须做出优化。

注意到:

dp[times][diff]是在dp[times][diff-1]的基础上前半段各个项减一,后半段各个项加一得到的,所以可以维护一个前缀和数组pre[i],那么

dp[times][diff]=dp[times][diff-1]+(pre[diff+2k]-pre[diff-1])-(pre[diff-1]-pre[(diff-1)-2k-1])

可以在O(1)的时间内完成,优化后的代码时间复杂度为O(kt2),代码如下:

 1 #include<iostream>
 2 #include<cmath>
 3 #define M 1000000007LL
 4 #define TIME 105
 5 #define DIFF 500000
 6 #define BASE 250000
 7 using namespace std;
 8 typedef long long LL;
 9 LL a,b,k,t,ans;
10 LL dp[TIME][DIFF];
11 LL pre[DIFF];
12 int main(void){
13     cin>>a>>b>>k>>t;
14     dp[0][a-b+BASE]=1;
15     LL upper=a-b+BASE+2*k*t;
16     LL lower=a-b+BASE-2*k*t;
17     for(LL times=1;times<=t;++times){
18         for(LL diff=lower;diff<=upper;diff++)
19             pre[diff]=pre[diff-1]+dp[times-1][diff],pre[diff]%=M;
20         for(LL m=0;m<=2*k;m++){
21             LL add=-2*k+m;
22             if(add)dp[times][lower]
23                 +=(dp[times-1][lower+add]+dp[times-1][lower-add])*(m+1);
24             else dp[times][lower]+=dp[times-1][lower]*(m+1);
25             dp[times][lower]%=M;
26         }
27         for(LL diff=lower+1;diff<=upper;diff++){
28             dp[times][diff]=dp[times][diff-1]
29                 +(pre[min(upper,diff+2*k)]-pre[diff-1])
30                 -(pre[diff-1]-pre[max(lower,diff-1-2*k)-1]);
31             dp[times][diff]=(dp[times][diff]+M)%M;
32             //记得+M,减法模运算可能会出现负数
33         }
34     }
35     for(int i=BASE+1;i<=upper;++i)
36         ans=(ans+dp[t][i])%M;
37     cout<<ans<<endl;
38 }

 

这样的代码仍然可以优化:

1.可以用滚动数组来优化空间复杂度,从O(kt2)降低到O(kt),太懒没写╮(╯▽╰)╭;

2.可以用快速傅里叶变换FFT优化时间复杂度,从O(kt2)继续降到O(kt lg(kt)),没学还不会写╮(╯▽╰)╭

 

//昨天去面试微软俱乐部被嘲讽=。= 定个目标吧,这学期div2稳定4题怎么样?

 

转载于:https://www.cnblogs.com/barrier/p/5875456.html

class NeuralDictionary(nn.Module): def __init__(self, num_keys, d_model, update_stride=100, update_size=5, momentum=0.2, top_k=None, temperature=0.1, topk_context_size=3, use_context_sim=True, use_meta_attention=True, value_update_ratio=0.5, usage_decay=0.9): # 新增参数 super().__init__() # 参数校验 assert 0 <= value_update_ratio <= 1, "value_update_ratio should be in [0,1]" assert 0 <= usage_decay <= 1, "usage_decay should be in [0,1]" # 配置参数 self.num_keys = num_keys self.d_model = d_model self.update_stride = update_stride self.update_size = update_size self.momentum = momentum self.value_update_ratio = value_update_ratio # values更新比例 self.top_k = top_k self.temperature = temperature self.topk_context_size = topk_context_size self.use_context_sim = use_context_sim self.use_meta_attention = use_meta_attention self.usage_decay = usage_decay # 使用计数器衰减系数 # 内存初始化 self.keys = nn.Parameter(torch.empty(num_keys, d_model)) nn.init.kaiming_normal_(self.keys, mode='fan_in', nonlinearity='relu') # 值内存独立初始化 self.values = nn.Parameter(torch.empty(num_keys, d_model)) nn.init.xavier_normal_(self.values) # 动态状态跟踪 self.register_buffer('usage_counter', torch.zeros(num_keys)) self.register_buffer('update_count', torch.tensor(0)) # 查询偏置 self.query_bias = nn.Parameter(torch.zeros(d_model)) # 元注意力优化:简化结构 if use_meta_attention: # 使用单层参数化注意力 self.meta_attention = nn.Linear(num_keys, num_keys) else: self.meta_attention = None def forward(self, query, context=None): # 维度校验 assert query.dim() == 2, "Input query must be 2D (batch_size, d_model)" # 查询增强 query = query + self.query_bias # (B, d_model) # 高效注意力计算 attn_scores = torch.einsum('bd,kd->bk', query, self.keys) # (B, K) attn_scores /= self.temperature # 上下文相似度增强 if self.use_context_sim and context is not None: assert context.shape == query.shape, "Context must match query shape" # 使用优化的余弦相似度计算 context_sim = F.cosine_similarity( context.unsqueeze(1), # (B, 1, d) self.keys.unsqueeze(0),# (1, K, d) dim=-1 ) # (B, K) attn_scores *= context_sim # Top-K稀疏化 if self.top_k is not None and self.top_k < self.num_keys: topk_scores, topk_indices = torch.topk(attn_scores, self.top_k, dim=-1) attn_scores = torch.full_like(attn_scores, float('-inf')).scatter_(-1, topk_indices, topk_scores) # 元注意力变换 if self.use_meta_attention: attn = F.softmax(self.meta_attention(attn_scores), dim=-1) else: attn = F.softmax(attn_scores, dim=-1) # 内存读取 memory_output = torch.einsum('bk,kd->bd', attn, self.values) # (B, d) # 训练时状态更新 if self.training: self._update_usage(attn) if context is not None: self._update_memory(context) return memory_output
03-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值