//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2022.9.17校内模拟赛解题报告

校内模拟赛解题报告

T1

思路

基本的思路就是纯模拟,但很明显会 TLE,所以考虑一下如何优化,我们可以提前处理一下走完一边指令后的坐标,然后直接用 k 来除以当前的指令长度然后给 xy 加上得数乘以一遍指令后 xy 的移动距离,然后直接再去模拟 k 对指令长度的取模的值就好了,指令最多只有 5000 个字母,不用担心会 TLE。

代码

#include<bits/stdc++.h>
#define N 100100
using namespace std;
string s;
int n,len,chao;
int main()
{
	freopen("sanae.in","r",stdin);
	freopen("sanae.out","w",stdout);
	cin>>s>>n;
	len=s.size();
	chao=n/len;
	n=n%len;
	int x=0,y=0,dx=0,dy=0;
	for(int i=0;i<len;i++)
	{
		if(s[i]=='E')dx++;
		else if(s[i]=='S')dy--;
		else if(s[i]=='W')dx--;
		else if(s[i]=='N')dy++;
	}
	x+=chao*dx;
	y+=chao*dy;
	for(int i=0;i<n;i++)
	{
		if(s[i]=='E')x++;
		else if(s[i]=='S')y--;
		else if(s[i]=='W')x--;
		else if(s[i]=='N')y++;
	}
	cout<<x<<" "<<y<<endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2

思路

首先我们需要把图存起来,然后跑最短路,但是这个最短路的 dis 数组里面不是用来存放花费,而是存放当前从 1 到 n 需要多少次帮助,也就是当前 k 的值,然后主函数二分 k 的值知道有一个可行的答案为止,具体注释看代码。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define N 20010
using namespace std; 
struct sb{int to,w,next;}e[N];//存放边 
int n,p,k,head[N],cnt,dis[N],vis[N];//dis存放走到当前点需要用多少次帮助,vis标记是否找到最短路 
void add(int x,int y,int z)//加边操作 
{
	e[++cnt].to=y;
	e[cnt].next=head[x];
	e[cnt].w=z;
	head[x]=cnt;
}
int check(int x)//二分查找答案
{
	memset(dis,INF,sizeof(dis));//重置dis数组 
	queue<int>q; 
	dis[1]=0;//起点不用走
	q.push(1);//起点入列 
	while(!q.empty())//只要队列不空 
	{
		int qwq=q.front();//qwq取出队头元素 
		q.pop();//弹出 
		vis[qwq]=0;
		for(int i=head[qwq];i;i=e[i].next)//遍历每一个与之相连的边 
		{
			int o;//o是大于当前点的边权的数量 
			if(e[i].w>x)//如果当前边大于x 
			  o=dis[qwq]+1;//o的值就是上一点能存放的o的值加1 
			else//否则 
			  o=dis[qwq];//o不变,等于上一个起点dis的值 
			if(o<dis[e[i].to])//如果o的值小于当前终点的dis 
			{
				dis[e[i].to]=o;//替换 
				if(!vis[e[i].to])//如果当前的点没有在队列里 
				{
					q.push(e[i].to);//入列 
					vis[e[i].to]=1;//打上标记 
				}
			}
		}
	}
	if(dis[n]<=k)return 1;//如果当前终点的需要帮助次数小于k就返回可行 
	else return 0;
}
int main()
{
	cin>>n>>p>>k;
	for(int i=1;i<=p;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);//存无向图 
		add(y,x,z);
	}
	int ans=-1;//ans存放答案,一开始假设不可以 
	int l=0,r=1e7;//二分答案 
	while(l<=r)
	{
		int mid=(r+l)>>1;
		if(check(mid))//check检验答案是否可行 
		  r=mid-1,ans=mid;
		else l=mid+1;
	}
	cout<<ans<<endl;//输出答案 
	return 0;
 } 

T3

思路

明显的 dp 但我不会

考试的时候没想出来直接打了一个爆搜,然后就过了答案是 0 的点。。。

正解:用 f 数组来表示当到达 i 时所有人等待的最少的时间,j表示当前的 i 由哪个时刻转移而来,然后很容易推出状态转移方程:

\(f[i]=min(f[i],f[j]+\sum_{t[k]<=i}j-t[k])\)

把右边的拆开就是:

\(cnt[i]*j-sum[i]\)

再加上一些小优化就可以 A 掉了,具体看代码。

代码

#include<bits/stdc++.h>
#define N 4000010
using namespace std;
int n,m,mt,t;
int f[N],ti[N],sum[N];//f存放当i时刻的最少等待时间,ti存放每一个人等待的前缀和,sum存放每一个节点所有人时间和的前缀和 
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)//枚举每一个人 
	{
		cin>>t;
		ti[t]++;//累加 
		sum[t]+=t;//累加当前所有人的时间 
		mt=max(t,mt);//取最大时间 
	}
	for(int i=1;i<=mt+m;i++)//处理前缀和 
	  ti[i]+=ti[i-1],sum[i]+=sum[i-1];
	for(int i=1;i<=mt+m;i++)//枚举每一个时间 
	{
		if(i>=m&&ti[i]==ti[i-m])//处理中间没有人的情况 
		{
		    f[i]=f[i-m];
			continue;
		}
		f[i]=ti[i]*i-sum[i];//处理当前人都在等待的情况 
		for(int j=max(i-2*m+1,0);j<=i-m;j++)//遍历每一个能由j推出i的情况,注意此时间段摆渡车只能来回一次 
		  f[i]=min(f[i],f[j]+(ti[i]-ti[j])*i-(sum[i]-sum[j]));//当前点的值更新为原来的值和由j转移过来的值的较小值
	}
	int ans=1e9;
	for(int i=mt;i<=mt+m;i++) 
	  ans=min(ans,f[i]);//求最小值 
	cout<<ans<<endl;//输出答案 
	return 0;//好习惯 
}
posted @ 2022-09-17 18:30  北烛青澜  阅读(34)  评论(0)    收藏  举报