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;
}
posted @ 2022-05-28 16:34  superPG  阅读(131)  评论(0编辑  收藏  举报