20190521测试总结

在这个非常特殊的日子来了一次虐狗的考试。(奸笑

这次测试不是考的很好,因为没有考过qt啊啊啊!!!!

考试历程
上次考试一下跌入谷底,是因为每一道题都兼顾导致每一道题都没能深刻思考,分数不堪入目。于是我下定决心要扳回一城。
第一题的DP状态设置的时候设了好多种情况,很多种写在程序上时写着写着就转移不下去了,或者说有什么后效性吧,最后好不容易才花了1小时多才really推出来,能写出我就很开心了嘿嘿,不过耗时还是挺长,还是要多练。
第二题就是个版子我不多说。
第三题咋一看还以为什么高超的dp,然后我又写不出,于是只能快速写了个dfs外加一大堆优化然鹅并没有什么卵子用(╥╯^╰╥),完美TLE。【本来应该是有30分的暴力分,后来我发现一个坑,当输入同布流和斯堪福scanf printf连用时可能会产生一些奇奇怪怪的东西,于是我决定以后再也不用同布流了(╬ ̄皿 ̄)=○】
第四题一看见最大的最短立马想到二分答案+spfa(),考试中想到sm大佬讲过类似的二分思想,于是check函数一会便推出,然后本以为皆大欢喜可以A了,MM的正解居然是最小生成树?!excuse me???

不多说,还是练的太少,掌握不到位,唉。

一.马拉松

题目链接

在二维平面上有N个点,从(x1,y1)到(x2,y2)的代价为|x1-x2|+|y1-y2|。
求从1号点出发,按从1到N的顺序依次到达每个点的最小总代价。
你有K次机会可以跳过某个点,不允许跳过1号点或N号点。

【题解】:类似于导弹拦截的状态设置,如果不加一些状态的限制便很难转移。

我们设 f[i][j] 表示目前位置为第i个点,前面一定跳过了j个点的最小代价(j<=i-2,第一个点和当前点不能跳)

那么很明显i 号点是由前 i-1号点拓展过来的,那么转移便很容易了。

code

//1
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct fuc
{
	int x,y;
}a[510];
int n,k,f[501][501]; //目前所在位置为i,前面已经跳过了j个,的最小值 
int main()
{
	freopen("marathon.in","r",stdin);
	freopen("marathon.out","w",stdout);
    scanf("%d%d",&n,&k);
    memset(f,10,sizeof(f));
    for(int i=1;i<=n;i++)
      {
      	int x,y;
      	scanf("%d%d",&a[i].x,&a[i].y);
	  }
	f[1][0]=0;
    for(int i=2;i<=n;i++)
      for(int j=0;j<=min(i-2,k);j++)
        for(int z=1;z<=i-1;z++)
          if(j>=i-z-1) f[i][j]=min(f[i][j],f[z][j-i+z+1]+abs(a[i].x-a[z].x)+abs(a[i].y-a[z].y));
	//i-z-1表示z--i中间的数的个数,从z跳到i中间跳过的数量
	printf("%d",f[n][k]);
	return 0;
}



二.Sta

题目大意:给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大。

【题解】:板子题,树上一点到其他点的距离和最大,不会的可以见我的博客不会请点我的树形dp系列。

code

//2
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll top=0,first[1000001],n,size[1000001],f[1000001],g[1000001],maxx=-100,ans=0;
struct fuc
{
	int x;
	int next;
}a[2001000];
void add(int x,int y)
{
	top++;
	a[top].x=y;
	a[top].next=first[x];
	first[x]=top;
}
void dfs(int fa,int x)
{
	size[x]=1;
	for(int i=first[x];i;i=a[i].next)
	{
		if(a[i].x==fa) continue;
		dfs(x,a[i].x);
		size[x]+=size[a[i].x];
		f[x]+=f[a[i].x]+size[a[i].x];
 	}
}
void dfsrfy(int fa,int x)
{
  if(x!=1) g[x]=g[fa]+(n-size[x]-size[x]);
  for(int i=first[x];i;i=a[i].next)
  {
     if(a[i].x==fa) continue;
	 dfsrfy(x,a[i].x);	
  }     	
}
int main()
{ freopen("sta.in","r",stdin);
  freopen("sta.out","w",stdout); 
  scanf("%d",&n);
  for(int i=1;i<=n-1;i++)
    {
      int xx,yy;	
      scanf("%d%d",&xx,&yy);
	  add(xx,yy);
	  add(yy,xx);	
	}
	dfs(0,1);
	g[1]=f[1];
	dfsrfy(0,1);
	for(int i=1;i<=n;i++)
	 {
	   if(g[i]>maxx)
	   {
	      maxx=g[i];
		  ans=i;   	
		} 	
	 }
	 printf("%d\n",ans);	
} 

三.穿越

由于某OJ尚未公布此题(却拿来做考试题 ),所以我只能自己搬题面了(;´д`)ゞ、

【问题描述】
小x听说某大国很流行穿越,于是他就想写一个关于穿越的剧本。
闲话休提。话说小x穿越到了某一个剑与魔法的大陆。因为如此这般,所以小x从维娜艾那里得到了预言。小x一共被告知了若干件按顺序结算的事件。这些事件分为两类:战役事件(CASE)、穿越回去事件(END)。
战役事件可以选择是否参加,参加了之后会获得一个RP和一定的金钱。
每个END事件发生需要至少参加一定数量的战役事件(足够的RP值)。特别的是,END事件如果满足要求(当下小x已经有了足够RP值),就会强制(必须)发生。
小x希望在大陆玩个够,所以他要求只有最后一个END事件会发生,且小x希望满足上述情况下,能够获得最多的金钱,所以求助于你。

【输入】
第一行一个数N,表示输入文件有多少行。

接下来每一行用空格隔开一个字符和一个整数。

字符为“c”表示战役事件,接下来的整数表示这次涨RP顺带有多少钱;

字符为“e”表示穿越回去事件,接下来的整数代表至少要涨多少RP。

最后一个事件保证是END事件。

【输出】
第一行一个整数,最多金钱数目。
若不可能则输出-1。

【数据范围】
30%的数据满足 N<=20
60%的数据满足 N<=1,000
100%的数据满足 N<=200,000
每次涨RP事件赏金不超过10,000
穿越事件的要求不超过200,000

这种数量级范围我用dfs如果不用输入同步流的话是可以拿30分,尽管有很多没什么用的优化。

题解】:yzx大佬考场中写出了正解却头文件打错爆0(滑稽 ),告诉我们什么,想出了正解也不要扬扬得意,没准你一时眼瞎文件名打错了呢对不(๑╹◡╹)ノ"""
不过我是真没想到这居然是一个贪心。

明显,碰到一个end事件那么我们就要从前面选择不超过此rp值个事件使价值最大,那么我们就先把价值最小的扔掉不要,用一个小根堆来维护这样的属性。碰到c事件便push进去,碰到一个end事件就把pop是的小根堆里的元素数<rp 【由于最后一个end它没有限制,这时我们不用pop了,特判一下便可】

code

#include<bits/stdc++.h>
using namespace std;
priority_queue< int,vector<int>,greater<int> >q;
int n;
long long sum=0;
struct fuc
{
	int x,v;
}a[200001];
int main()
{   freopen("dragons.in","r",stdin);
    freopen("dragons.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	 {  char s;
	    int v;  
	 	cin>>s>>v;
	 	if(s=='c') a[i].x=1;
	    a[i].v=v;
	 }
	int tot=1;
	for(int i=1;i<=n;i++)
	{
		if(a[i].x==1) q.push(a[i].v);
		else  
		   if(i!=n)
		  {   
		    
			 while(q.size()>=a[i].v)
			 {
				 q.pop();
			 }
		  }
		if(i==n&&q.size()<a[i].v) //最终可能还回不了家(虽然某OJ没此类数据...)
			{
				printf("-1\n");
				return 0;
			}
	}
	while(!q.empty())
	{
		sum+=q.top();
		q.pop();
	}
	printf("%lld",sum);
 } 

这件事还启示我们一个东西:要用脚能打出小根堆的巴拉巴拉的玩意儿。不然考试即使知道正解不是爆零就是 不会写。

四.最小瓶颈路

某OJ上又没有!!!
【问题描述】
给定一个包含n个节点和 m 条边的图,每条边有一个权值。
你的任务是回答k个询问,每个询问包含两个正整数 s和 t 表示起点和终点,要求寻找从s到t的一条路径,使得路径上权值最大的一条边权值最小。

【输入格式】
第一行包含三个整数n 、m、k,分别表示 n个节点, m 条路径,k 个询问。
接下来 m行,每行三个整数u,v,w, 表示一个由 u 到v 的长度为w 的双向边。
再接下来 k 行,每行两个整数 s,t,表示询问从 s连接到t 的所有路径中单边长度最大值的最小值。

【数据范围与提示】
对于30% 的数据 n<=100,m<=1000,k<=100,w<=1000
对于 70% 的数据 n<=1000,m<=10000,k<=1000,w<=100000
对于100% 的数据 n<=1000,m<=100000,k<=1000,w<=10000000
本题可能会有重边。
为了避免 Special Judge,本题所有的w 均不相同。

最大的最小,不是二分答案么???
完美TLE o(▼皿▼メ;)o

谁能想到正解是最小生成树%%%(wc有毒)

Kruskal算法原理便是把图中所有的边按从小到大排序,依次枚举,如果当前边的两端点已经连通便不操作,否则利用并查集将两点所在的连通块连在一起。

再来仔细看看我们的问题,如何计算两点之间最小瓶颈路上最大边的长度…

首先明确无向图中最小瓶颈路一定是在最小生成树中,除非本来就不连通。

我们知道Kruskal算法是按照边由小到大的顺序将点连接起来的,我们要求这两个点组成的图中最大的边最小,那么也就是只要两个点在一棵树中了,那么最初将他们所在连通量连接的边就是我们要求的距离了。十分的巧妙,仔细想想。

所以只需要在建树过程中离线的查询k组疑问,判断此时他们是否连在一起了,存储答案便OK。

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int fa[1001],n,m,k,vis[1001],ans[1001]={},num;
struct fuc
{
	int x,y,v;
}a[100010];
struct fuk
{
	int x,y;
}b[1001];
bool mycmp(fuc a,fuc b)
{
	return  a.v<b.v;
}
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
   freopen("package.in","r",stdin);
   freopen("package.out","w",stdout);
   scanf("%d%d%d",&n,&m,&k);
   for(int i=1;i<=n;i++) fa[i]=i;
   for(int i=1;i<=m;i++)
     {
     	int x,y,v;
     	scanf("%d%d%d",&x,&y,&v);
     	a[i].x=x,a[i].y=y,a[i].v=v;
	 }
	 sort(a+1,a+m+1,mycmp);
	 for(int i=1;i<=k;i++)
	 {  
	    int x,y;
	 	scanf("%d%d",&x,&y);
	 	b[i].x=x,b[i].y=y;
	 }
	 num=n;//当前连通量数目
	 for(int i=1;i<=m;i++)
	  {
	   	int u=find(a[i].x),v=find(a[i].y);
	   	if(u!=v)
	   	{
	   	  fa[u]=v;
		  num--;
		  for(int j=1;j<=k;j++)
		    if((!ans[j])&&(find(b[j].x)==find(b[j].y)))
		       ans[j]=a[i].v;	
		}
		if(num==1) break;
	  }
	for(int i=1;i<=k;i++)
	  {
	  	if(!ans[i]) printf("-1\n");
	  	else printf("%d\n",ans[i]);
	  }
   return 0;
}

妙啊

考试能暴露出近期学习的不足,我们不能以一次考试的失误而失去信心,而应该去反思最近的学习是否积极思考,认真听讲,及时复习,用考试这一测试手段来进一步提升自我。

Fighting!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值