《关于我们WA题太多导致皮卡丘看不下去把妙蛙种子电死于是我们现在叫不听不听,|

superPG

园龄:5年1个月粉丝:2关注:10

2022-05-28 16:34阅读: 137评论: 0推荐: 0

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;
}

正确写法:

  1. 将循环时间 i 更改为 从0开始,这对于模运算来说是必要的。
  2. 由于每次是对脉冲强度I进行累加,所以当这次脉冲完成后,I需要清零。
  3. 也正因为2,所以I的两个维度中,时间在第一维度,结点在第二维度。
  4. 除此之外,想要过最后的数据,就不能使用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 中国大陆许可协议进行许可。

posted @   superPG  阅读(137)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 いつも何度でも 伊藤サチコ
いつも何度でも - 伊藤サチコ
00:00 / 00:00
An audio error has occurred.

呼んでいる 胸のどこか奥で

いつも心踊る 夢を見たい

かなしみは 数えきれないけれど

その向こうできっと あなたに会える

繰り返すあやまちの そのたび ひとは

繰り返すあやまちの そのたび ひとは

ただ青い空の 青さを知る

果てしなく 道は続いて見えるけれど

この両手は 光を抱ける

さよならのときの 静かな胸

さよならのときの 静かな胸

ゼロになるからだが 耳をすませる

生きている不思議 死んでいく不思議

花も風も街も みんなおなじ

nananan lalala lululu

nananan lalala lululu

呼んでいる 胸のどこか奥で

いつも何度でも 夢を描こう

かなしみの数を 言い尽くすより

同じくちびるで そっとうたおう

閉じていく思い出の そのなかにいつも

閉じていく思い出の そのなかにいつも

忘れたくない ささやきを聞く

こなごなに砕かれた 鏡の上にも

新しい景色が 映される

はじまりの朝の静かな窓

はじまりの朝の静かな窓

ゼロになるからだ 充たされてゆけ

海の彼方には もう探さない

輝くものは いつもここに

わたしのなかに 見つけられたから

nananan lalala lululu

nananan lalala lululu