zoukankan      html  css  js  c++  java
  • NOIP2018提高组模拟题(二)

    咒语(curse)

    Description

    亮亮梦到自己来到了魔法城堡,但一扇巨大的石门阻拦了他通向城堡内的路。 正当他沮丧之际,突然发现门上有一处机关,机关上有一张很长的纸条。

    亮亮拿起纸条的一端,只见上面写着打开机关的方法:“打开机关需要念动 符咒,咒语是一串长为 L 的由 0 和 1 组成的字符串。在这张长纸条上列了 n 个 长为 L 的字符串,正确的咒语即是在纷繁的 2^L 种字符串中,与这些纸条上的 字符串相异度之和最小,并且在满足这一条件下,0 的个数最多的字符串。两个 字符串的相异度定义为对应位置不相等的字符对的个数。如‘011’和‘001’的 相异度为 1,因为它们有且只有第二个位置上的字符不相等。”

    亮亮拉起纸条,只觉得纸条似乎永远也拉不完。这上面有着数以万计的字符 串,而每一个字符串的长度也或百或千,以人力看来是无法得到正确的咒语。你 能帮帮他,让他得以进入魔法城堡,一窥其中的奥秘吗?

    Input

    第一行为一个数字 N 。

    接下来的 N 行,每行为一个长为 L 的 01 字符串。数据保证 N 个字符串等长。

    Output

    只有一行,是一个长为 L 的字符串 S,即为正确的咒语。

    别看题面这么长,这不是一个渣题 emmm

    记录每一位上(1)的个数,判断当前位上是(1)的个数多还是(0)的个数多,决定输出(1)(0)

    (pos[i])代表(i)位置上(1)的个数,则(n-pos[i])就代表(i)位置上(0)的个数.

    代码

    #include<cstdio>
    #include<cstring>
    #define R register
    using namespace std;
    int n,pos[20008],len;
    char s[20008];
    inline void calc(char *ch)
    {
    	if(!len)len=strlen(ch);
    	for(R int i=0;i<len;i++)
    		if(ch[i]=='1')pos[i]++;
    }
    int main()
    {
    	//freopen("curse.in","r",stdin);
    	//freopen("curse.out","w",stdout);
    	scanf("%d",&n);
    	for(R int i=1;i<=n;i++)
    	{
    		scanf("%s",s);
    		calc(s);
    	}
    	for(R int i=0;i<len;i++)
    	{
    		if(pos[i]>(n-pos[i]))putchar('1');
    		else putchar('0');
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    神光(light)

    Description

    亮亮成功地念出了咒语,石门缓缓地自动移开,一道道绚丽的神光从城堡内 激射而出。亮亮好奇而又兴奋地走入了城堡中,迎面有一座极长的魔法阵。

    魔法阵可以看作一条直线,它被均匀地分成了 1 000 000 000 个位置,一个位 置可以看成是一个格子。有些位置上筑有法坛,一共 N 座。亮亮只有破了眼前 的魔法阵,才能继续前进,而欲破法阵,必须毁掉所有的法坛。

    亮亮身前有两根法杖:一根颜色血红,能发红色神光,光芒可以笼罩连续 L 个位置,并摧毁这 L 个位置上所有的法坛,最多使用 R 次;另一根颜色碧绿, 能发绿色神光,光芒可以笼罩连续 2L 个位置,并摧毁这 2L 个位置上所有的法 坛,最多使用 G 次。

    法杖的神奇之处在于,L 的值必须由亮亮事先设定好,并且一经设定,便无 法更改。亮亮需要在规定的次数下摧毁所有法坛,并且使得 L 最小。

    Input

    第一行三个整数 N, R, G。

    第 i (2<=i<=n+1) 行一个整数Ai,表示第 i 座法坛的位置。

    Output

    只有一个整数,表示 L 的最小值 。

    数据范围

    对于 50%的数据,N <= 100;

    对于 100%的数据,1 <= N <= 2000,1 <= R, G, Ai <= 1,000,000,000。

    T2竟然是最难的题 emmm,

    首先二分不是很难想到吧.(二分我们的(L)

    看到(R,G leq 1e9),完了,岂不是要打暴力.

    后来发现当(R+G>N)的时候,直接输出(1)即可 emm.(老子有的是,随便放 ╭(╯^╰)╮

    然后问题就转变为了(R,G leq 2000),考虑(DP)

    (f[i][j])代表使用了(i)次红灯,(j)次红灯干掉了多少法坛.

    显然我们考虑如何传递上面的状态,

    则我们

    (p[i])代表从(i)位置用红光能干到最靠右的位置

    (q[i])代表从(i)位置用绿光能干到最靠右的位置

    先给出状态转移方程:

    [f[i][j]=max(p[f[i-1][j]+1],f[i][j])\ f[i][j]=max(q[f[i][j-1]+1],f[i][j]) ]

    考虑到(f[i][j])代表着什么。

    我们对(A)数组进行排序之后,得到的(f[i][j])必定递增(从(1)(i)

    则我们可以根据这个性质写出状态转移方程.

    解释了里面为什么是(f[i-1][j]),应该不是很难理解.

    代码

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #define int long long
    #define N 2008
    #define clear(a) memset(a,0,sizeof a)
    #define R register
    using namespace std;
    inline void in(int &x)
    {
    	int f=1;x=0;char s=getchar();
    	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    	x*=f;
    }
    int g,G,n,a[N],ans;
    int p[N],q[N],f[2][N*1000];//f[i][j]i次红光,j次绿光,能毁多少法坛 
    inline bool ok(int x)
    {
    	clear(p);clear(q);clear(f);
    	for(R int i=1;i<=n;i++)
    	{
    		for(R int j=i;j<=n;j++)
    		{
    			if(a[j]-a[i]+1<=x)p[i]=j;
    			if(a[j]-a[i]+1<=x*2)q[i]=j;	
    		}
    	}
    	p[n+1]=q[n+1]=n;
    	for(R int i=0;i<=g;i++)
    	{
    		int op=i&1;
    		for(R int j=0;j<=G;j++)
    		{
            	if(i!=0)f[op][j]=max(p[f[op^1][j]+1],f[op][j]);
            	if(j!=0)f[op][j]=max(q[f[op][j-1]+1],f[op][j]); 
    		}
    	}	
    	return f[g&1][G]==n;
    }
    signed main()
    {
    	//freopen("light.in","r",stdin);
    	//freopen("light.out","w",stdout);
    	in(n),in(g),in(G);
    	if(g+G>n)
    	{
    		puts("1");
    		return 0;
    	}
    	for(R int i=1;i<=n;i++)in(a[i]);
    	sort(a+1,a+n+1);
    	int l=0,r=1e9;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(ok(mid))r=mid-1,ans=mid;
    		else l=mid+1;
    	}
    	printf("%lld",ans);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    迷宫(maze)

    Description

     破了魔法阵后,亮亮进入了一座迷宫。这座迷宫叫做“梦境迷宫”,亮亮只 有走出这座迷宫,才能从睡梦中醒来。

     梦境迷宫可以用无向图来表示。它共有 n 个点和 m 条双向道路,每条道路 都有边权,表示通过这条道路所需的时间,且每条道路可以多次经过。亮亮位于 一号点,而出口则是 n 号点。原本,亮亮该找到一条最短路,快速冲出迷宫,然 而,梦境迷宫的特殊之处在于,如果沿着最短路到达出口,亮亮就会永远陷入梦 境。因此,亮亮必须寻找一条次短路。次短路的长度须严格大于最短路(可以有 多条)的长度,同时又不大于所有除最短路外的道路的长度。

     你的任务,就是编写一个程序,帮助亮亮找到通向出口的次短路。

    Input

    第一行有两个整数 n、m,表示迷宫内共有 n 个点,m 条边。

    接下来 m 行,每行三个整数 x、y、z,表示结点 x 和 y 之间连有一条边权为 z 的无向边。

    Output

    一个整数,表示次短路的长度。

    难得T3这么简单,傻逼题啊,直接跑一遍(Spfa)求次短路.

    考虑我们的次短路和最短路有关系,所以可以在跑最短路的时候跑出次短路.

    存在三种情况

    • 能更新最短路
    • 能更新次短路,但是不能更新最短路
    • 只能更新次短路

    分类讨论

    一.能更新最短路

     这时候我们的次短路直接继承最短路即可.

    二.能更新次短路,但是不能更新最短路

     如果我们到达当前点(u)所连的(v)的最短路无法被更新,显然当前边为一条不优边,我们可以用到达(u)的最短路通过当前边来更新到达(v)的次短路.(这一情况有些难理解.但是又不太难理解)

    三.只能更新次短路

    直接通过到达(u)的次短路更新到达(v)的次短路

    代码

    #include<cstdio>
    #include<queue>
    #include<cctype>
    #define R register
    #define int long long
    #define N 5555
    using namespace std;
    inline void in(int &x)
    {
    	int f=1;x=0;char s=getchar();
    	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    	x*=f;
    }
    int n,m,head[N],tot,dis[N],diss[N];
    bool vis[N];
    struct cod{int u,v,w;}edge[800008];
    inline void add(int x,int y,int z)
    {
    	edge[++tot].u=head[x];
    	edge[tot].v=y;
    	edge[tot].w=z;
    	head[x]=tot;
    }
    inline void spfa()
    {
    	for(R int i=1;i<=n;i++)dis[i]=diss[i]=2147483647666LL;
    	dis[1]=0;vis[1]=true;
    	queue<int>q;q.push(1);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();vis[u]=false;
    		for(R int i=head[u];i;i=edge[i].u)
    		{
    			if(dis[edge[i].v]>dis[u]+edge[i].w)
    			{
    				diss[edge[i].v]=dis[edge[i].v];
    				dis[edge[i].v]=dis[u]+edge[i].w;
    				if(!vis[edge[i].v])
    				{
    					vis[edge[i].v]=true;
    					q.push(edge[i].v);
    				}
    			}
    			if(dis[u]+edge[i].w>dis[edge[i].v] and diss[edge[i].v]>dis[u]+edge[i].w)
    			{
    				diss[edge[i].v]=dis[u]+edge[i].w;
    				if(!vis[edge[i].v])
    				{
    					vis[edge[i].v]=true;
    					q.push(edge[i].v);
    				}	
    			}
    			if(diss[edge[i].v]>diss[u]+edge[i].w)
    			{
    				diss[edge[i].v]=diss[u]+edge[i].w;
    				if(!vis[edge[i].v])
    				{
    					vis[edge[i].v]=true;
    					q.push(edge[i].v);
    				}
    			}
    		}
    	}
    	printf("%lld",diss[n]);
    }
    signed main()
    {
    	freopen("maze.in","r",stdin);
    	freopen("maze.out","w",stdout);
    	in(n),in(m);
    	for(R int i=1,x,y,z;i<=m;i++)
    	{
    		in(x),in(y),in(z);
    		add(x,y,z);add(y,x,z);
    	}
    	spfa();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    基于redis实现的延迟消息队列
    Redis实现求交集操作结果缓存的设计方案
    限流算法之漏桶算法、令牌桶算法
    Apache设置防DDOS模块mod_evasive
    FastCGI技术
    详解强大的SQL注入工具——SQLMAP
    nginx根据域名做http,https分发
    Nginx配置SSL证书部署HTTPS网站
    JProfiler学习笔记
    Mysql压测工具mysqlslap 讲解
  • 原文地址:https://www.cnblogs.com/-guz/p/9809537.html
Copyright © 2011-2022 走看看