20180908考试

T1

3806 -- 【模拟试题】升降梯上

Description

  开启了升降梯的动力之后,探险队员们进入了升降梯运行的那条竖直的隧道,映入眼帘的是一条直通塔顶的轨道、一辆停在轨道底部的电梯、和电梯内一杆控制电梯升降的巨大手柄。 
  Nescafe之塔一共有N层,升降梯在每层都有一个停靠点。手柄有M个控制槽,第i个控制槽旁边标着一个数Ci,满足C1<C2<C3<....<CM。如果Ci>0,表示手柄扳动到该槽时,电梯将上升Ci层;如果Ci<0,表示手柄扳动到该槽时,电梯将下降 -Ci 层;并且一定存在一个Ci=0,手柄最初就位于此槽中。注意升降梯只能在1~N层间移动,因此扳动到使升降梯移动到1层以下、N 层以上的控制槽是不允许的。 
  电梯每移动一层,需要花费2秒钟时间,而手柄从一个控制槽扳到相邻的槽,需要花费1秒钟时间。探险队员现在在1层,并且想尽快到达N层,他们想知道从1层到N层至少需要多长时间?

Input

  第一行两个正整数N、M。 
  第二行M个整数C1、C2......CM。

Output

  输出一个整数表示答案,即至少需要多长时间。若不可能到达输出-1。

Sample Input

6 3 -1 0 2

Sample Output

19

Hint

【样例说明】
  手柄从第二个槽扳到第三个槽(0扳到2),用时1秒,电梯上升到3层,用时4秒。 
  手柄在第三个槽不动,电梯再上升到5层,用时4秒。
  手柄扳动到第一个槽(2扳到-1),用时2秒,电梯下降到4层,用时2秒。
  手柄扳动到第三个槽(-1扳倒 2),用时2秒,电梯上升到6层,用时4秒。 
  总用时为(1+4)+4+(2+2)+(2+4)=19秒。 

【数据规模】
  对于30%的数据,满足1≤N≤10,2<=M<=5。 
  对于100%的数据,满足1≤N≤1000,2<=M<=20,-N<C1<C2<....<CM<N。

 

考场上写的O(n*m^2)的dp拿了90,本来期望100的……

定义:f[i][j]表示在第i层时手柄在j位置的最小代价

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
const int INF=0x3f3f3f3f,MAXX=100005;
int read()
{
   int s=0,bj=0;
   char ch=getchar();
   while(ch<'0'||ch>'9')bj|=(ch=='-'),ch=getchar();
   while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
   return bj?-s:s;
}
void printnum(int x)
{
	if(x>9)printnum(x/10);
	putchar(x%10^48);
}
void print(int x,char ch)
{
	if(x<0){putchar('-');x=-x;}
	printnum(x);putchar(ch);
}
int _min(int x,int y){return x<y?x:y;}
int abss(int x){return x>0?x:-x;}
int f[1005][25];//f[i][j]表示在第i层时把手在第j个槽里的最小代价 
int n,m,c[25],bj,minn=INF;
signed main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++){c[i]=read();if(c[i]==0)bj=i;}
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=m;i++)f[1][i]=abss(bj-i);
	for(int kk=1;kk<=n>>3;kk++)//由于扳过来那部分可能之前没有更新到,应该跑n遍dp,但是时间原因,所以只跑了n/8遍 
	for(int i=1;i<=n;i++)//枚举层 
	{
		for(int j=1;j<=m;j++)//枚举扳手位置 
		{
			for(int k=1;k<=m;k++)f[i][j]=_min(f[i][j],f[i][k]+abss(j-k));//当前层手柄在其他位置时扳过来的情况 
			if(i+c[j]>=1&&i+c[j]<=n)f[i+c[j]][j]=_min(f[i+c[j]][j],f[i][j]+(abss(c[j])<<1));//如果在这个手柄走,那么要判断它是否在1——n层,然后更新 
		}
	}
	for(int i=1;i<=m;i++)minn=_min(minn,f[n][i]);//在每个位置时的代价取min 
	if(minn>=INF)puts("-1");
	else print(minn,'\n');
	return 0;
}

 T2

2031 -- 【NOIP模拟试题】塔顶试探

Description

  探险队员们顺利乘坐升降梯来到了塔顶。塔顶有若干房间,房间之间连有通道。如果把房间看做节点,通道看做边的话,整个塔顶呈现一个有根树结构,并且每个房间的墙壁都涂有若干种颜色的一种。
  现在探险队员们在树根处,他们打算进一步了解塔顶的结构,为此,他们使用了一种特殊设计的机器人。这种机器人会从队员身边,也就是树根出发,之后对塔顶进行深度优先遍历。机器人每进入一个房间(无论是第一次进入还是返回),都会记录这个房间的颜色。最后,机器人会回到树根。
  显然,机器人会访问每个房间至少一次,并且穿越每条通道恰好两次(两个方向各一次),然后,机器人会得到一个颜色序列。但是,探险队员发现这个颜色序列并不能唯一确定塔顶的结构。现在他们想请你帮助他们计算,对于一个给定的颜色序列,有多少种可能的结构会得到这个序列。由于结果可能会非常大,你只需要输出答案对 10^9 取模之后的值。

Input

  输入文件包含一行,含有一个字符串,表示机器人得到的颜色序列。

Output

  输出一个整数表示答案。

Sample Input

ABABABA

Sample Output

5

Hint

  
考场爆搜……期望24,实际40
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
char ch[305],bj[305];
int len,n,fa[305],cnt,num;
void DFS(int x,int no)
{
	if(x==len){num++;if(num==1e9)num=0;return;}
	if(bj[fa[no]]==ch[x]){DFS(x+1,fa[no]);}
	if(cnt<n)
	{
		bj[++cnt]=ch[x];fa[cnt]=no;
		DFS(x+1,cnt);
		fa[cnt]=0;bj[cnt]=0;cnt--;
	}
}
int main()
{
	scanf("%s",ch+1);
	len=strlen(ch+1);n=(len+1)>>1;
	DFS(1,0);
	printf("%d\n",num);
	return 0;
}
正解居然是区间dp!?
定义:f[i][j]表示i到j段里的方案数
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
using namespace std;
const int mod=1e9;
char ch[305];
LL f[305][305];int n;
int main()
{
	scanf("%s",ch+1);n=strlen(ch+1);
	for(int i=1;i<=n;i++)f[i][i]=1;//初始化 
	for(int len=3;len<=n;len+=2)//枚举长度 ,易得len只有在奇数时才能有方案 
	{
		for(int l=1;l<=n-len+1;l++)//枚举l 
		{
			int r=len+l-1;
			if(ch[l]^ch[r])continue;//易得只有l和r相等时才能有方案 
			f[l][r]=f[l+1][r-1];
			for(int mid=l+2;mid<=r-2;mid++)if(ch[l]==ch[mid])f[l][r]=(f[l][r]+1ll*f[l+1][mid-1]*f[mid][r]%mod)%mod;//枚举中间点,并更新 ,因为是方案数,所以相乘 
		}
	}
	printf("%lld\n",f[1][n]);//(不开long long见祖宗) 
	return 0;
}

 T3

2368 -- 【模拟试题】礼物运送

Description

  机器人刚刚探查归来,探险队员们突然发现自己的脚下出现了一朵朵白云,把他们托向了空中。一阵飘飘然的感觉过后,队员们发现自己被传送到了一座空中花园。
  “远道而来的客人,我们是不是守护Nescafe之塔的精灵。如果你们想拜访护法和圣主的话,就要由我们引路。因此,你们是不是该给我们一点礼物呢?”
  队员们往远处一看,发现花园中由N各木箱,M条柔软的羊毛小路,每条小路的两个端点都是木箱,并且通过它需要ti的时间。队员们需要往每个木箱中放一份礼物,不过由于精灵们不想让花园被过多地踩踏,因此运送礼物的任务最多只能由两位探险队员完成。
  两位探险队员同时从1号木箱出发,可以在任意木箱处结束运送。运送完毕时,每只木箱应该被两位队员中至少一人访问过。运送任务所用的时间是两人中较慢的那个人结束运送任务的时间。请问这个时间最快是多少呢?

Input

  第一行两个整数N,M;
  接下来M行每行三个整数xi,yi,ti,表示xi和yi之间有一条用时ti的小路,小路是双向的。

Output

  输出所需的最短时间。

Sample Input

6 6
1 2 10
2 3 10
3 4 5
4 5 10
5 6 20
2 5 10

Sample Output

40

Hint

【数据范围与约定】
  对于50%的数据,1<=N<=9;
  对于100%的数据,1<=N<=18,1<=ti<=1000,1<=xi,yi<=N,两个木箱之间最多只有一条小路,不会有自己到自己的小路,保证所有木箱是连通的。
玄学floyd+搜索,期望100,实际10……
下来问了老师正解,才发现我考场上
正解是spfa+状压
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f,MAXX=100005;
int read()
{
   int s=0,bj=0;
   char ch=getchar();
   while(ch<'0'||ch>'9')bj|=(ch=='-'),ch=getchar();
   while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
   return bj?-s:s;
}
int n,m;
struct bian{int nx,to,v;}w[325];int h[19],cnt;
void AddEdge(int x,int y,int z){w[++cnt].to=y;w[cnt].nx=h[x];w[cnt].v=z;h[x]=cnt;}
int d[1<<19][19];//当前已走的集合为i,现在在j的最小代价 
bool vst[1<<19][19],bj[19];
struct node{int x,y;};
void spfa()
{
	memset(d,0x3f,sizeof(d));
	queue<node>q;
	d[1][1]=0;
	q.push((node){1,1});
	while(!q.empty())
	{
		int xx=q.front().x,yy=q.front().y;q.pop();vst[xx][yy]=0;
		for(int i=h[yy];i;i=w[i].nx)
		{
			int y=w[i].to,no=xx|(1<<(y-1));//往y点走 
			if(d[no][y]>d[xx][yy]+w[i].v)
			{
				d[no][y]=d[xx][yy]+w[i].v;
				if(!vst[no][y]){vst[no][y]=1;q.push((node){no,y});}
			}
		}
	}
}
void dfs(int x)
{
	if(bj[x])return;bj[x]=1;
	for(int i=1;i<=n;i++)d[x][0]=min(d[x][0],d[x][i]);//d[i][0]表示已经走了i集合的最小代价
	for(int i=1;i<=n;i++)//枚举下一步往哪个点走
	if(!(x&(1<<(i-1))))//如果这个点没有被走过 
	{
		int y=x|(1<<(i-1));
		dfs(y);
		d[x][0]=min(d[x][0],d[y][0]);
	}
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read(),z=read();
		AddEdge(x,y,z);AddEdge(y,x,z);
	}
	spfa();
//	for(int i=1;i<=(1<<n)-1;i++)
//	{
//		for(int j=1;j<=n;j++)cout<<d[i][j]<<" ";cout<<endl;
//	}
	dfs(0);//初始集合为空
	int ans=INF;
	for(int i=0;i<=(1<<n)-1;i++)
	ans=min(ans,max(d[i][0],d[(1<<n)-1-i][0])/*枚举每一种两个人走的情况*/);
	printf("%d\n",ans);
	return 0;
}

  

posted @ 2019-09-09 19:28  Peter_Rabbit  阅读(301)  评论(0编辑  收藏  举报