CSP-23-3 脉冲神经网络
原题链接:https://www.acwing.com/problem/content/4011/
大模拟
需要注意的地方还是挺多的:
- 以后写这种大模拟的时候,尽量不要使用STL。
- 注意多维数组存储,维度调换对事件的影响,特别是需要用memset的时候。
- 注意模运算,模运算的整个过程中,参与模运算的数一定一直保持在[0,mod)之间,不能出现从mod-1,跳到1去的情况。
第一版(纯暴力模拟 66分):
#include <bits/stdc++.h>
using namespace std;
// D > 0,所以不需要考虑拓扑序的问题
// 也就是说每个位置的 uv 只与 上一时刻 uv
// 和 当前时刻累计的 I 有关。
// 更进一步的,由于 D < 10,所以每个位置的数据只依赖于其在前10个时间段内的状态。
// 脉冲计算函数
static unsigned long nt = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
nt = nt * 1103515245 + 12345;
return ((unsigned)(nt / 65536) % 32768);
}
const int N = 1e3 + 10, M = N, T = 1e3 + 10;
int n, s, p, t;
double derta_t;
int cnt[N];
int cnt_n, cnt_p, cnt_s;
double v[N][T], u[N][T], I[N][T], a[N], b[N], c[N], D[N];
int r[N];
struct Edge {
int ed;
double w;
int d;
};
vector<Edge> edge[2 * N];
// 计算某个结点某个时刻的uv值
void calc(int uu, int tt) {
v[uu][tt] = v[uu][tt - 1] + derta_t *(0.04 * v[uu][tt - 1] * v[uu][tt - 1] + 5.0 * v[uu][tt - 1] + 140.0 - u[uu][tt -
1]) + I[uu][tt];
u[uu][tt] = u[uu][tt - 1] + derta_t *a[uu] * (b[uu] * v[uu][tt - 1] - u[uu][tt - 1]);
}
/*
void add(int st,int ed,double tmp_w,double tmp_d)
{
e[idx] = ed, w[idx] = tmp_w, d[idx] = tmp_d, ne[idx] = h[st], idx++;
}
*/
void add(int st, int ed, double tmp_w, int tmp_d) {
edge[st].push_back({ed, tmp_w, tmp_d});
}
int main() {
scanf("%d %d %d %d", &n, &s, &p, &t);
scanf("%lf", &derta_t);
while (cnt_n != n) {
int tmp_n;
double tmp_v, tmp_u, tmp_a, tmp_b, tmp_c, tmp_d;
scanf("%d %lf %lf %lf %lf %lf %lf", &tmp_n, &tmp_v, &tmp_u, &tmp_a, &tmp_b, &tmp_c, &tmp_d);
for (int i = cnt_n; i < cnt_n + tmp_n; i++) {
v[i][0] = tmp_v;
u[i][0] = tmp_u;
a[i] = tmp_a;
b[i] = tmp_b;
c[i] = tmp_c;
D[i] = tmp_d;
}
cnt_n += tmp_n;
}
for (int i = 0; i < p; i++) {
scanf("%d", &r[i]);
}
for (int i = 0; i < s; i++) {
int tmp_s, tmp_t;
double tmp_w;
int tmp_d;
scanf("%d %d %lf %d", &tmp_s, &tmp_t, &tmp_w, &tmp_d);
add(tmp_s, tmp_t, tmp_w, tmp_d);
}
for (int i = 1; i <= t; i++) {
for (int j = 0; j < n + p; j++) {
if (j < n) {
calc(j, i);
if (v[j][i] >= 30.0) {
//传递信号,记得更新cnt
cnt[j]++;
for (int k = 0; k < edge[j].size(); k++) {
I[edge[j][k].ed][i + edge[j][k].d] += edge[j][k].w;
}
v[j][i] = c[j];
u[j][i] = u[j][i] + D[j];
}
} else {
// 根据随机数传递信号
int id = j - n;
if (r[id] > myrand()) {
for (int k = 0; k < edge[j].size(); k++) {
I[edge[j][k].ed][i + edge[j][k].d] += edge[j][k].w;
}
}
}
}
}
//printf("%lf %lf %lf\n", v[0][5], u[0][5], I[0][5]);
int min_cnt = t + 1, max_cnt = -1;
double max_v = -1e9, min_v = 1e9;
for (int i = 0; i < n; i++) {
min_cnt = min(min_cnt, cnt[i]);
max_cnt = max(max_cnt, cnt[i]);
min_v = min(min_v, v[i][t]);
max_v = max(max_v, v[i][t]);
}
printf("%.3lf %.3lf\n%d %d\n", min_v, max_v, min_cnt, max_cnt);
return 0;
}
误入歧途:
半小时模拟出66分,然后发现时间是瓶颈,考虑到将,结点的u和v值只需要保存上一个状态,所以用滚动数组优化。结点u和时间t进行hash存储(什么nt想法啊),为了表达方便,写成了unordered_map,那能性吗??!!unordered_map是hash用的,比手写hash要慢,hash都已经是常数复杂度了,unordered_map还只是平均意义上的常数复杂度,算法是 1e8的,时间3s,肯定连常数复杂度的算法都不能再加进去了。包括用vector存边,肯定比邻接表慢。
然后又花好长时间错处积极丑陋的 滚动数组+unordered_map,显然超时,还是66分(hh)。
第二版(误入歧途写法 66分):
#include <bits/stdc++.h>
using namespace std;
// D > 0,所以不需要考虑拓扑序的问题
// 也就是说每个位置的 uv 只与 上一时刻 uv
// 和 当前时刻累计的 I 有关。
// 更进一步的,由于 D < 10,所以每个位置的数据只依赖于其在前10个时间段内的状态。
// 脉冲计算函数
static unsigned long nt = 1;
typedef long long LL;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
nt = nt * 1103515245 + 12345;
return ((unsigned)(nt / 65536) % 32768);
}
const int N = 1e3 + 10, M = N, T = 1e3 + 10;
int n, s, p, t;
double derta_t;
int cnt[N];
int cnt_n, cnt_p, cnt_s;
double v[N][110], u[N][110], I[N][110], a[N], b[N], c[N], D[N];
int r[N];
struct Edge {
int ed;
double w;
int d;
};
vector<Edge> edge[2 * N];
unordered_map<LL, double> mp;
// 计算某个结点某个时刻的uv值
void calc(int uu, int tt) {
double II = 0.0;
if (mp.count(uu * 100001 + tt)) {
II = mp[uu * 100001 + tt];
}
tt = tt % 2;
int pret = 1 ^ tt;
v[uu][tt] = v[uu][pret] + derta_t *(0.04 * v[uu][pret] * v[uu][pret] + 5.0 * v[uu][pret] + 140.0 - u[uu][pret]) +
II;
u[uu][tt] = u[uu][pret] + derta_t *a[uu] * (b[uu] * v[uu][pret] - u[uu][pret]);
}
/*
void add(int st,int ed,double tmp_w,double tmp_d)
{
e[idx] = ed, w[idx] = tmp_w, d[idx] = tmp_d, ne[idx] = h[st], idx++;
}
*/
void add(int st, int ed, double tmp_w, int tmp_d) {
edge[st].push_back({ed, tmp_w, tmp_d});
}
int main() {
scanf("%d %d %d %d", &n, &s, &p, &t);
scanf("%lf", &derta_t);
while (cnt_n != n) {
int tmp_n;
double tmp_v, tmp_u, tmp_a, tmp_b, tmp_c, tmp_d;
scanf("%d %lf %lf %lf %lf %lf %lf", &tmp_n, &tmp_v, &tmp_u, &tmp_a, &tmp_b, &tmp_c, &tmp_d);
for (int i = cnt_n; i < cnt_n + tmp_n; i++) {
v[i][0] = tmp_v;
u[i][0] = tmp_u;
a[i] = tmp_a;
b[i] = tmp_b;
c[i] = tmp_c;
D[i] = tmp_d;
}
cnt_n += tmp_n;
}
for (int i = 0; i < p; i++) {
scanf("%d", &r[i]);
}
for (int i = 0; i < s; i++) {
int tmp_s, tmp_t;
double tmp_w;
int tmp_d;
scanf("%d %d %lf %d", &tmp_s, &tmp_t, &tmp_w, &tmp_d);
add(tmp_s, tmp_t, tmp_w, tmp_d);
}
for (int i = 1; i <= t; i++) {
for (int j = 0; j < n + p; j++) {
if (j < n) {
calc(j, i);
if (v[j][i % 2] >= 30.0) {
//传递信号,记得更新cnt
cnt[j]++;
for (int k = 0; k < edge[j].size(); k++) {
if (!mp.count(edge[j][k].ed * 100001 + i + edge[j][k].d)) {
mp.insert({edge[j][k].ed * 100001 + i + edge[j][k].d, edge[j][k].w});
} else
mp[edge[j][k].ed * 100001 + i + edge[j][k].d] += edge[j][k].w;
}
v[j][i % 2] = c[j];
u[j][i % 2] = u[j][i % 2] + D[j];
}
} else {
// 根据随机数传递信号
int id = j - n;
if (r[id] > myrand()) {
for (int k = 0; k < edge[j].size(); k++) {
if (!mp.count(edge[j][k].ed * 100001 + i + edge[j][k].d)) {
mp.insert({edge[j][k].ed * 100001 + i + edge[j][k].d, edge[j][k].w});
} else
mp[edge[j][k].ed * 100001 + i + edge[j][k].d] += edge[j][k].w;
}
}
}
}
}
//printf("%lf %lf %lf\n", v[0][5], u[0][5], I[0][5]);
int min_cnt = t + 1, max_cnt = -1;
double max_v = -1e9, min_v = 1e9;
for (int i = 0; i < n; i++) {
min_cnt = min(min_cnt, cnt[i]);
max_cnt = max(max_cnt, cnt[i]);
min_v = min(min_v, v[i][t % 2]);
max_v = max(max_v, v[i][t % 2]);
}
printf("%.3lf %.3lf\n%d %d\n", min_v, max_v, min_cnt, max_cnt);
return 0;
}
正确写法:
- 将循环时间 i 更改为 从0开始,这对于模运算来说是必要的。
- 由于每次是对脉冲强度I进行累加,所以当这次脉冲完成后,I需要清零。
- 也正因为2,所以I的两个维度中,时间在第一维度,结点在第二维度。
- 除此之外,想要过最后的数据,就不能使用STL,写的时候脑抽,以为要用vector才能存储从某一点出发的边的信息,其实邻接表不正好是这样表示的吗?(感觉被自己蠢到)
正确写法 CSP 和 AcWing均100分:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010;
const double INF = 1e8;
int n, s, p, T;
double dt;
int h[N], e[N], D[N], ne[N], idx;
double W[N], v[N], u[N], a[N], b[N], c[N], d[N];
int r[N], cnt[N];
double I[1024][N / 2];
static unsigned long _next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
_next = _next * 1103515245 + 12345;
return((unsigned)(_next/65536) % 32768);
}
void add(int a, int b, double c, int d)
{
e[idx] = b, W[idx] = c, D[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &s, &p, &T);
scanf("%lf", &dt);
for (int i = 0; i < n;)
{
int rn;
scanf("%d", &rn);
double vv, uu, aa, bb, cc, dd;
scanf("%lf%lf%lf%lf%lf%lf", &vv, &uu, &aa, &bb, &cc, &dd);
for (int j = 0; j < rn; j ++, i ++ )
{
v[i] = vv, u[i] = uu, a[i] = aa, b[i] = bb, c[i] = cc, d[i] = dd;
}
}
for (int i = n; i < n + p; i ++ ) scanf("%d", &r[i]);
int mod = 0;
while (s -- )
{
int a, b, d;
double c;
scanf("%d%d%lf%d", &a, &b, &c, &d);
add(a, b, c, d);
mod = max(mod, d + 1);
}
for (int i = 0; i < T; i ++ )
{
int t = i % mod;
for (int j = n; j < n + p; j ++ )
if (r[j] > myrand())
{
for (int k = h[j]; ~k; k = ne[k])
{
int x = e[k];
I[(t + D[k]) % mod][x] += W[k];
}
}
for (int j = 0; j < n; j ++ )
{
double vv = v[j], uu = u[j];
v[j] = vv + dt * (0.04 * vv * vv + 5 * vv + 140 - uu) + I[t][j];
u[j] = uu + dt * a[j] * (b[j] * vv - uu);
if (v[j] >= 30)
{
for (int k = h[j]; ~k; k = ne[k])
{
int x = e[k];
I[(t + D[k]) % mod][x] += W[k];
}
cnt[j] ++ ;
v[j] = c[j], u[j] += d[j];
}
}
memset(I[t], 0, sizeof I[t]);
}
double minv = INF, maxv = -INF;
int minc = INF, maxc = -INF;
for (int i = 0; i < n; i ++ )
{
minv = min(minv, v[i]);
maxv = max(maxv, v[i]);
minc = min(minc, cnt[i]);
maxc = max(maxc, cnt[i]);
}
printf("%.3lf %.3lf\n", minv, maxv);
printf("%d %d\n", minc, maxc);
return 0;
}
本文作者:superPG
本文链接:https://www.cnblogs.com/superPG/p/16321007.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?