• 【noip 模拟赛curse,light,maze】 题解


    2018.10.16

    总结:考的不好

    原因:
    1.考的时候没状态,读题读不进去
    2.考的时候不仔细,该得分没得到

    T1:curse

    1、咒语
    (curse.pas/c/cpp)
    【题目描述】
    亮亮梦到自己来到了魔法城堡,但一扇巨大的石门阻拦了他通向城堡内的路。正当他沮丧之际,突然发现门上有一处机关,机关上有一张很长的纸条。亮亮拿起纸条的一端,只见上面写着打开机关的方法:“打开机关需要念动符咒,咒语是一串长为 L 的由 0 和 1 组成的字符串。在这张长纸条上列了 n 个长为 L 的字符串,正确的咒语即是在纷繁的 2^L 种字符串中,与这些纸条上的字符串相异度之和最小,并且在满足这一条件下,0的个数最多的字符串。两个字符串的相异度定义为对应位置不相等的字符对的个数。如‘011’和‘001’的相异度为1,因为它们有且只有第二个位置上的字符不相等。”亮亮拉起纸条,只觉得纸条似乎永远也拉不完。这上面有着数以万计的字符串,而每一个字符串的长度也或百或千,以人力看来是无法得到正确的咒语。你能帮帮他,让他得以进入魔法城堡,一窥其中的奥秘吗?

    【输入格式】

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

    【输出格式】

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

    【样例输入】

    4
    01011
    01001
    01101
    10111

    【样例输出】

    01001

    【数据规模】

    对于 20%的数据,N<=5;
    对于 60%的数据,N<=100;
    对于 100%的数据,1<=N<=1000,1<=L<=1000。

    题解:

    细心读题,不难做出正确判断。坑点:注意当n=1时判断的边界。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 1001;
    int num[maxn], n;
    char s[maxn];
    int main()
    {
    	freopen("curse.in","r",stdin);
    	freopen("curse.out","w",stdout);
    	cin>>n;
    	int len;
    	for(int i = 1; i <= n; i++)
    	{
    		cin>>s;
    		len = strlen(s);
    		for(int j = 0; j < len; j++)
    		if(s[j] == '1') num[j]++;
    	}
    	for(int i = 0; i < len; i++)
    	{
    		if(num[i] > n/2) cout<<"1";
    		else cout<<"0";
    	}
    	return 0;
    }
    

    T2:light

    2、神光
    (light.pas/c/cpp)

    【题目描述】

    亮亮成功地念出了咒语,石门缓缓地自动移开,一道道绚丽的神光从城堡内激射而出。亮亮好奇而又兴奋地走入了城堡中,迎面有一座极长的魔法阵。魔法阵可以看作一条直线,它被均匀地分成了1 000 000 000 个位置,一个位置可以看成是一个格子。有些位置上筑有法坛,一共 N 座。亮亮只有破了眼前的魔法阵,才能继续前进,而欲破法阵,必须毁掉所有的法坛。亮亮身前有两根法杖:一根颜色血红,能发红色神光,光芒可以笼罩连续 L个位置,并摧毁这 L 个位置上所有的法坛,最多使用 R 次;另一根颜色碧绿,能发绿色神光,光芒可以笼罩连续 2L 个位置,并摧毁这 2L 个位置上所有的法
    坛,最多使用G次。法杖的神奇之处在于,L 的值必须由亮亮事先设定好,并且一经设定,便无法更改。亮亮需要在规定的次数下摧毁所有法坛,并且使得L最小。

    【输入格式】

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

    【输出格式】

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

    【样例输入】

    3 1 1
    22
    1
    7

    【样例输出】

    4

    【样例解释】

    亮亮将L 设为 4,并用红色神光笼罩 21-24 位置,用绿色神光笼罩 1-8 位置。

    【数据规模】

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

    题解:

    第一眼二分。

    但是,考试不仔细读题。感觉巨大的数据范围二分个鸡毛。

    仔细看。R,G的大小远远比N大的时候答案就是1。

    所以我们把数据范围缩小到2000。

    不妨设p[k] 表示使用一次激光发射L距离能摧毁到第几座法坛 ,q[k] 表示使用一次激光发射2*L距离能摧毁到第几座法坛,可以预处理得到p,q

    dp[i][j]表示使用i次红光,j次绿光能摧毁最多的法坛数,那么二分返回true的条件是dp[R][G] == n

    有状态转移方程:(dp[i][j] = max(p[dp[i-1][j]+1], q[dp[i][j-1]+1]))

    p[dp[i-1][j]+1] 是红光在dp中前一次到下一次能摧毁的最远

    q[dp[i][j-1]+1] 是绿光在dp中前一次到下一次能摧毁的最远

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 2018;
    int dp[maxn][maxn], a[maxn], p[maxn], q[maxn], R,N,G, ans;
    bool check(int L)
    {
        memset(dp, 0, sizeof(dp));
        memset(p, 0, sizeof(p));
        memset(q, 0, sizeof(q));
        for(int i = 1; i <= N; i++)
            for(int j = i; j <= N; j++)
            {
                if(a[j] - a[i] + 1 <= L) p[i] = j;
                if(a[j] - a[i] + 1 <= L*2) q[i] = j;
            }
        p[N+1] = q[N+1] = N;
        for(int i = 0; i <= R; i++)
            for(int j = 0; j <= G; j++)
            {
            	if(i > 0) dp[i][j] = max(p[dp[i-1][j]+1], dp[i][j]);
    		if(j > 0) dp[i][j] = max(q[dp[i][j-1]+1], dp[i][j]); 
    	}
                
        return dp[R][G] == N;
    }
    int main()
    {
        freopen("light1.in","r",stdin);
        freopen("light.out","w",stdout);
        cin>>N>>R>>G;
        if(R+G > N)
        {
            cout<<"1";
            return 0;
        }
        for(int i = 1; i <= N; i++)
        cin>>a[i];
        sort(a+1, a+1+N);
        a[0] = 0;
        int l = 1, r = a[N] - a[1] + 1;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            if(check(mid)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        cout<<ans;
        return 0;
    }
    

    T3:maze

    3、迷宫
    (maze.pas/c/cpp)

    【题目描述】

    破了魔法阵后,亮亮进入了一座迷宫。这座迷宫叫做“梦境迷宫”,亮亮只有走出这座迷宫,才能从睡梦中醒来。梦境迷宫可以用无向图来表示。它共有 n 个点和 m 条双向道路,每条道路都有边权,表示通过这条道路所需的时间,且每条道路可以多次经过。亮亮位于一号点,而出口则是n号点。原本,亮亮该找到一条最短路,快速冲出迷宫,然而,梦境迷宫的特殊之处在于,如果沿着最短路到达出口,亮亮就会永远陷入梦境。因此,亮亮必须寻找一条次短路。次短路的长度须严格大于最短路(可以有多条)的长度,同时又不大于所有除最短路外的道路的长度。你的任务,就是编写一个程序,帮助亮亮找到通向出口的次短路。

    【输入格式】

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

    【输出格式】

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

    【样例输入】

    4 4
    1 2 2
    2 4 4
    2 3 3
    3 4 4

    【样例输出】

    9

    【样例解释】

    最短路:1 -> 2 -> 4 (长度为2+4=6)
    次短路:1 -> 2 -> 3 -> 4 (长度为2+3+4=9)

    【数据规模】

    对于100%的数据,1 <= n <= 5000,1 <= m <= 100,000。
    对于100%的数据,1 <= z <= 5000,z表示无向边的边长。

    求次短路。我是傻逼跑了1,n两次spfa后枚举点,枚举边就能过。

    因为会有到点n后走回一条最短的边再回去的情况。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 100010;
    struct edge{
    	int len, to, from, next;
    }E[maxn<<2];
    int cnt, head[maxn];
    void add(int u, int v, int w)
    {
    	E[++cnt].from = u; 
    	E[cnt].len = w; 
    	E[cnt].next = head[u]; 
    	head[u] = cnt; 
    	E[cnt].to = v;
    }
    int diss[maxn], dise[maxn], s, e, ans = 0x7fffffff, n, m, now;
    bool viss[maxn], vise[maxn];
    queue<int> qs, qe;
    void SPFAs()
    {
    	memset(diss, 127, sizeof(diss));
    	memset(viss, 0, sizeof(viss));
    	qs.push(s);
    	diss[s] = 0;
    	viss[s] = 1;
    	while(!qs.empty())
    	{
    		int now = qs.front(); qs.pop();
    		viss[now] = 0;
    		for(int i = head[now]; i != -1; i = E[i].next)
    		{
    			if(diss[E[i].to] > diss[now] + E[i].len)
    			{
    				diss[E[i].to] = diss[now] + E[i].len;
    				if(!viss[E[i].to])
    				{
    					qs.push(E[i].to);
    					viss[E[i].to] = 1;
    				}
    			}
    		}
    	}
    }
    void SPFAe()
    {
    	memset(dise, 127, sizeof(dise));
    	memset(vise, 0, sizeof(vise));
    	qe.push(e);
    	dise[e] = 0;
    	vise[e] = 1;
    	while(!qe.empty())
    	{
    		int now = qe.front(); qe.pop();
    		vise[now] = 0;
    		for(int i = head[now]; i != -1; i = E[i].next)
    		{
    			if(dise[E[i].to] > dise[now] + E[i].len)
    			{
    				dise[E[i].to] = dise[now] + E[i].len;
    				if(!vise[E[i].to])
    				{
    					qe.push(E[i].to);
    					vise[E[i].to] = 1;
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	memset(head, -1, sizeof(head));
    	scanf("%d%d",&n,&m);
    	s = 1, e = n;
    	for(int i = 1; i <= m; i++)
    	{
    		int u, v, w;
    		scanf("%d%d%d",&u,&v,&w);
    		add(u, v, w); add(v, u, w);
    	}
    	SPFAs();
    	SPFAe();
    	for(int i = 1; i <= n; i++)	
    		for(int j = head[i]; j != -1; j = E[j].next)
    		{
    			int v = E[j].to, w = E[j].len;
    			now = diss[i] + dise[v] + w;
    			if(now < ans && now > diss[n])
    			ans = now;
    		}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    附:在poj3255有次短路原题。

    //MisakaAzusa
    //dsbdsb2003.

  • 相关阅读:
    (二)Maven的使用--安装配置
    (一)Maven介绍
    (二)Monkey自动化脚本
    App测试--专项测试
    Java基础--(三)运算符
    Vue.js 介绍入门
    NodeJS 入门第三天(Express框架)
    NodeJS 入门第二天(EJS模板)
    《前端架构设计》读后感
    NodeJS 入门第一天
  • 原文地址:https://www.cnblogs.com/MisakaAzusa/p/9800052.html
Copyright © 2020-2023  润新知