测试总结:19/2/13 Contest#1081:NOIp2016 提高组 Day1

版权声明:转载请附带原文链接,请勿随意删除原文内容,允许少量格式和/或内容修改,谢谢! https://blog.csdn.net/weixin_37661548/article/details/87211957

前言

  • 本博客中的代码大多不是正解(因为作者太蒻了),本博客侧重根据测试点规模骗分方法的分析。也许以后作者能力上来了,会补上正解。内容浅显,文笔拙劣,望dalao轻喷 q w q qwq

T1 玩具谜题

题目链接

参考代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;

struct node
{
	int aspect;
	string job;
}info[MAXN];

struct command
{
	int aspect,cnt;
}cmd[MAXN];

int n,m;
int command[MAXN][2];

void init()
{
	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= n;++i)
	{
		qread(info[i].aspect);
		getline(cin,info[i].job);
	}
	for (int i = 1;i <= m;++i)
	{
		qread(cmd[i].aspect);
		qread(cmd[i].cnt);
	}
}

void work()
{
	int cur = 1,nxt = 0;
	for (int i = 1;i <= m;++i)
	{
		int h = cmd[i].cnt;
		if (info[cur].aspect == 0)
		{
			if (cmd[i].aspect == 0)
			{
				nxt = cur - h;
				if (nxt <= 0)
				{
					nxt += n;
				}
			}
			else
			{
				nxt = cur + h;
				if (nxt > n)
				{
					nxt = nxt - n;
				}
			}
		}
		else
		{
			if (cmd[i].aspect == 0)
			{
				nxt = cur + h;
				if (nxt > n)
				{
					nxt = nxt - n;
				}
			}
			else
			{
				nxt = cur - h;
				if (nxt <= 0)
				{
					nxt += n;
				}
			}
		}
		cur = nxt;
	}
	cout << info[cur].job;
}

int main()
{
	init();
	work();
	return 0;
}

分析

模拟,分析略。


T2 天天爱跑步

题目链接

参考代码(60pts)

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100000;

struct edge
{
	int to,nxt;
}info[MAXN << 1];

int n,m,e,ui,vi;
int head[MAXN],appear[MAXN],s[MAXN],t[MAXN],cntPlayer[MAXN][2],ans[MAXN];
bool inQueryStack[MAXN];
vector<int> query[MAXN];

inline void addedge(int from,int to,int wgt)
{
	info[++e].to = to;
	info[e].nxt = head[from];
	head[from] = e;
}

inline void addedge(int from,int to)
{
	info[++e].to = to;
	info[e].nxt = head[from];
	head[from] = e;
}

void init()
{
	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= n - 1;++i)
	{
		qread(ui);
		qread(vi);
		addedge(ui,vi);
		addedge(vi,ui);
	}
	for (int i = 1;i <= n;++i) qread(appear[i]);
	for (int i = 1;i <= m;++i)
	{
		qread(s[i]);
		qread(t[i]);
		cntPlayer[s[i]][0]++;
		cntPlayer[t[i]][1]++;
	}
}

void dfs(int u,int f,int d)
{
	for (int i = head[u];i;i = info[i].nxt)
	{
		int v = info[i].to;
		if (v == f) continue;
		dfs(v,u,d + 1);
		cntPlayer[u][1] += cntPlayer[v][1];
	}
	if (d == appear[u]) ans[u] += cntPlayer[u][1];
}

void dfs2(int u,int f,int d)
{
	inQueryStack[u] = true;
	for (int i = head[u];i;i = info[i].nxt)
	{
		int v = info[i].to;
		if (v == f) continue;
		dfs2(v,u,d + 1);
	}
	int Size = query[d].size();
	for (int i = 0;i < Size;++i)
	{
		if (inQueryStack[query[d][i]])
		{
			ans[query[d][i]] += cntPlayer[u][0];
		}
	}
	inQueryStack[u] = false;
}

void prepare(int u,int f,int d)
{
	for (int i = head[u];i;i = info[i].nxt)
	{
		int v = info[i].to;
		if (v == f) continue;
		prepare(v,u,d + 1);
	}
	if (d + appear[u] <= 100000) query[d + appear[u]].push_back(u);
}

namespace Solve
{
	void solve1()
	{
		for (int i = 1;i <= n;++i)
		{
			printf("%d ",!appear[i] ? cntPlayer[i][0] : 0);
		}
	}
	
	void solve2()
	{
		for (int i = 1;i <= n;++i)
		{
			printf("%d ",cntPlayer[i][0]);
		}
	}
	
	void solve3()
	{
		puts("stO LRL52 Orz");
	}
	
	void solve4()
	{
		dfs(1,0,0);
		for (int i = 1;i <= n;++i)
		{
			printf("%d ",ans[i]);
		}
	}
	
	void solve5()
	{
		prepare(1,0,0);
		dfs2(1,0,0);
		for (int i = 1;i <= n;++i)
		{
			printf("%d ",ans[i]);
		}
	}
	
	void solve6()
	{
		puts("stO LRL52 Orz");
	}
}

void work()
{
	if (n == 991) Solve::solve1();
	else if (n == 992) Solve::solve2();
	else if (n == 993) Solve::solve3();
	else if (n == 99995) Solve::solve4();
	else if (n == 99996) Solve::solve5();
	else Solve::solve6();
}

int main()
{
	init();
	work();
	return 0;
}

分析

  • appear[n]:观察者n出现时间。

  • s[n],t[n]:玩家n的起点和终点。

  • cntPlayer[n][2]:节点n是多少玩家的起点和终点。

  • ans[MAXN]:保存答案。

  • inQueryStack[MAXN]:查询栈。

  • query[n]:保存可以查询到深度为n的节点的观察者所在的节点编号。

  • 测试点#1~#2:由于每个人的起点即终点,则只有出现时间为0的观察者才可以观察到玩家。统计每个节点是多少个玩家的起点(即cntPlayer[i][0]),若这个节点的观察者出现时间为0,则输出统计值,否则输出0。

  • 测试点#3~#4:每个观察者的出现时间为0,此时玩家除了起点哪都不能去,故直接输出每个节点是多少个玩家的起点即可。

  • 测试点#9~#12:所有玩家的起点为1,则每个节点的深度即为玩家走到这一节点所需时间,则若观察者的深度等于其出现时间,就可以观察到玩家。

    注意cntPlayer[u][1] += cntPlayer[v][1];一句:

在这里插入图片描述
设节点3的观察者出现时间为1,两玩家的终点分别为3和7,执行上一句后,cntPlayer[3][1]由1变为2,这是因为两玩家一起向下走,同时经过节点3,被观测到。

故在这个子任务中,一节点的cntPlayer[][1]值应为其所有子节点的cntPlayer[][1]值之和

  • 测试点#13~#16:所有玩家的终点为1,玩家是从底向上走的。每个观察者可以观察到从dep[观察者深度] + 观察者出现时间 这一层出发的玩家。

    query[d + appear[u]].push_back(u);这句保存这样一个信息:d + appear[u]这一层的所有节点都可以被观察者u所观察到

    接着就通过一遍DFS,将每个观察者所能够观察到的玩家个数计算出来。没有玩家出发的节点也参与了计算,但并不会对结果造成影响,因为这些节点的
    cntPlayer[u][0]值为0。

    此处inQueryStack[MAXN]就发挥作用:以上图为例,当遍历到节点6时,观察者1、3对应的答案(ans[1]、ans[3])都应该加1,但与观察者3同层、且出现时间相同的的观察者2不应该被增加,因为节点6显然不会被其观测到。

    怎么办呢?按照DFS顺序,遍历到节点4或5时,inQueryStack[2] == true,而到节点6时,inQueryStack[2]已经为false,此时inQueryStack[3] == true。加一个判断即可。

  • LRL52是作者旁边的神犇。


T3 换教室

题目链接

参考代码#1(AC)

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 2005,MAXE = 90005,MAXV = 305;

int n,m,v,e,ui,vi,wi;
int mp[MAXV][MAXV],c[MAXN],d[MAXN];
double k[MAXN],dp[MAXN][MAXN][2];

void init()
{
	freopen("in.txt","r",stdin);
	scanf("%d%d%d%d",&n,&m,&v,&e);
	memset(mp,0x3f,sizeof(mp));
	for (int i = 1; i <= n; ++i) qread(c[i]);
	for (int i = 1; i <= n; ++i) qread(d[i]);
	for (int i = 1; i <= n; ++i) scanf("%lf",&k[i]);
	for (int i = 1; i <= v; ++i) mp[i][i] = 0;
	for (int i = 1; i <= e; ++i)
	{
		qread(ui);
		qread(vi);
		qread(wi);
		if (ui == vi || mp[ui][vi] < wi) continue;
		mp[ui][vi] = mp[vi][ui] = wi;
	}
	for (int k = 1; k <= v; k++)
		for (int i = 1; i <= v; ++i)
			for (int j = 1; j < i; ++j)
				if (mp[i][j] > mp[i][k] + mp[k][j])
					mp[j][i] = mp[i][j] = mp[i][k] + mp[k][j];
	for (int i = 0; i <= n; ++i)
		for (int j = 0; j <= m; ++j)
			for (int c = 0; c <= 1; ++c)
				dp[i][j][c] = 0x3f3f3f3f;
	dp[1][0][0] = dp[1][1][1] = 0;
}

void work()
{
	for (int i = 2; i <= n; ++i)
	{
		for (int j = 0; j <= m; ++j)
		{
			dp[i][j][0] = min(dp[i - 1][j][0] +
							  mp[c[i - 1]][c[i]],
			                  dp[i - 1][j][1] +
							  mp[d[i - 1]][c[i]] * k[i - 1] +
							  mp[c[i - 1]][c[i]] * (1 - k[i - 1]));
			if (j != 0)
			{
				dp[i][j][1] = min(dp[i - 1][j - 1][0] +
								  mp[c[i - 1]][c[i]] * (1 - k[i]) +
				                  mp[c[i - 1]][d[i]] * k[i],
				                  dp[i - 1][j - 1][1] +
								  mp[c[i - 1]][c[i]] * (1 - k[i - 1]) * (1 - k[i]) +
				                  mp[c[i - 1]][d[i]] * (1 - k[i - 1]) * k[i] +
				                  mp[d[i - 1]][c[i]] * k[i - 1] * (1 - k[i]) +
				                  mp[d[i - 1]][d[i]] * k[i - 1] * k[i]);
			}
		}
	}
	double ans = INT_MAX;
	for (int j = 0; j <= m; ++j)
	{
		for (int c = 0; c <= 1; ++c)
		{
			ans = min(ans,dp[n][j][c]);
		}
	}
	printf("%.2lf",ans);
}

int main()
{
	init();
	work();
	return 0;
}

分析

前人之述尽矣

参考代码#2(28pts)

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 2005,MAXE = 90005,MAXV = 305;

int n,m,v,e,ui,vi,wi;
int mp[MAXV][MAXV],c[MAXN],d[MAXN];
double k[MAXN],dp[MAXN][MAXN][2];

void init()
{
	freopen("in.txt","r",stdin);
	scanf("%d%d%d%d",&n,&m,&v,&e);
	memset(mp,0x3f,sizeof(mp));
	for (int i = 1; i <= n; ++i) qread(c[i]);
	for (int i = 1; i <= n; ++i) qread(d[i]);
	for (int i = 1; i <= n; ++i) scanf("%lf",&k[i]);
	for (int i = 1; i <= v; ++i) mp[i][i] = 0;
	for (int i = 1; i <= e; ++i)
	{
		qread(ui);
		qread(vi);
		qread(wi);
		if (ui == vi || mp[ui][vi] < wi) continue;
		mp[ui][vi] = mp[vi][ui] = wi;
	}
	for (int k = 1; k <= v; k++)
		for (int i = 1; i <= v; ++i)
			for (int j = 1; j < i; ++j)
				if (mp[i][j] > mp[i][k] + mp[k][j])
					mp[j][i] = mp[i][j] = mp[i][k] + mp[k][j];

}

void work()
{
	double ans = 0;
	for (int i = 2;i <= n;++i)
	{
		ans += mp[c[i - 1]][c[i]];
	}
	printf("%.2lf",ans);
}

int main()
{
	init();
	work();
	return 0;
}

分析

  • m == 0时,不可以申请换教室,用 F l o y d Floyd 求出1~n的最短路即可。

总结

  • Day1三道题,除了T1签到题之外,若T2不会正解、T3不会概率DP,暴力分有 100 + 60 + 28 = 188 100+60+28=188 分,离四川一等奖分数线(255)只有67分,Day2拿67分,难度就比较小了。
  • 深刻感觉自己的骗分能力不够,今后要努力夯实基础,提高代码能力。

猜你喜欢

转载自blog.csdn.net/weixin_37661548/article/details/87211957
今日推荐