【第二十题】军训日记:查寝(北理工/北京理工大学/程序设计方法与实践/小学期 )

description

 

小军的军训进行到了一半了,今天军训教官搞了一波突然袭击,进行了一个寝的查。  提前了解到查寝消息的小军准备进行一波整理归纳,来使自己的寝室变得更加整洁。具体来说,小军有n件物品,放在n个盒子里,第i个盒子有物品i,小军会进行m次整理,第i次整理,小军会依次在第x个盒子顶拿走物品放入第y个盒子内,直至第x个盒子完全搬空。比如第1个盒子自顶向下有物品1、2,第2个盒子有物品3,将盒子1内的物品搬入盒子2内后结果是: 第1个盒子没有物品,第2个盒子自顶向下是2、1、3  现在,小军告诉你n还有m次操作具体是什么,你能告诉他最后每个盒子内有几个物品,他们具体是什么么?  

输入:  一个正整数n代表盒子和物品数,一个正整数m代表整理归纳的次数  接下来m行输入,一行两个正整数x y,代表用上述的方法将盒子x的物品搬到盒子y里  1≤n≤10^5, 1≤m≤10^6, 1≤x,y≤n  题目保证x != y  

输出;  有n行输出  第i行,先输出一个正整数k,表示第i个盒子内的物品数,接下来输出n个数,表示第i个盒子自顶向下的物品标号

注意:  行末无空格,文末有回车。

思路

这是一个兄弟的思路 

一、 确定思路

从箱子中移动物品的操作符合栈先进后出的特点,因此一开始很容易想到用栈。然而用栈来模拟只能将物品一个个移动,很遗憾在本题的数据规模下会超时。

对这一思路进行优化,我们将物品从箱子x移动到y中时,其实可以把x中物品一次性倒过来再放到y中。也就是设一个头指针一个尾指针,将x和y头头相接即可,因此想到用双向链表来实现。

二、程序实现简述

1、定义结构体,包括指向前一个元素的指针prior和指向后一个元素的指针next

typedef struct node{

     int t;

struct node *prior;

    struct node *next;

}box;

2、声明头指针数组和尾指针数组,代表不同的箱子

box *head[100010],*tail[100010];

3、建立双向链表(与建立单向链表类似,略)

 这里需要自己去学了。

4、移动箱子

这里是关键,具体代码请独立思考,指针移来移去还是很有意思的

5、输入答案

       这里又是一个重要的点,因为今日之prior和next已非昔日之prior和next,出现了反向连接的情况。解决方案是:

既然箱子里的数都不同,那么两个指针指向的两头总有一个我已经输出过了,那么就向没输出过的方向移动。

①    声明两个变量记录当前输出的值和之前输出的值:int cur,las;

②    在循环中:

                cur=q->t;

                if(q->next->t==las)q=q->prior;

                else q=q->next;

                las=cur;

三、注意事项

1、数组声明为全局变量

3、小心NULL->next或NULL->prior的出现,会RE。特别注意如果箱子x已经空了以及搬入空箱子y的特殊情况代码是否适用,不适用需要特判

另一个兄弟的思路:请注意,不要给所有节点加翻转标记,否则会把最后一个用例TLE掉。

TLE原因就是,如果每次翻转都要把原来的盒子里的物品都更改翻转标记,那么算法复杂度就是n^2,那必然会有TLE的,事实上,这个阴间的小学期,所有n^2的算法都不可能ac

所以推荐用后一个(他本人也用的后一种算法,一看就是最优解(本人如是说))

 

最后一个兄弟的思路:使用数组取巧,适合没有双向链表基础的同学,但是这个方法虽然很香(一个acm大佬如是说),但是我还是觉得应该用双向链表,学真本事。 

 加翻转标记的代码

这个是我第一次做出来的成果

//考虑多种1情况
//1:两个有的盒子互相倒,细分一下,做出1到n,n到1,n到n,1到1的数据
//2:从有的盒子到没有的盒子,细分一下,做出1到0,n到0的数据
//3:没想到吧,真的有从空盒子里拿东西这种操作,所以需要排除
#include<stdio.h>
#include<stdlib.h>
#define WIDTH 100001
typedef struct node { 
	int num;
	struct node* next;
	struct node* pre;
	int isturned;
}node;
node* head[WIDTH], *tail[WIDTH];
int size[WIDTH];
int main(void)
{
//	freopen("input.txt", "r", stdin);
	int i, j, n, m, from, to;
	scanf("%d %d", &n, &m);
	//初始化盒子
	for (i = 1; i <= n; i++)
	{
		node* temp = (node*)malloc(sizeof(node));
		if (temp == NULL)
		{
			puts("malloc error");
			exit(0);
		}

		temp->num = i;
		temp->isturned = 0;
		temp->next = NULL;
		temp->pre = NULL;

		head[i] = temp;
		tail[i] = temp;
		size[i] = 1;
	}
	//移动m次
	for (i = 1; i <= m; i++)
	{
		scanf("%d %d", &from, &to);
		if (head[from] == NULL)//从无开始,相当于无操作
			continue;
		else if (head[to] != NULL)//从有到有
		{
			int from_turned = head[from]->isturned;//记录翻转状态,防止反转了以后链接出现问题
			int to_turned = head[to]->isturned;
			node* body = head[from], * pri = head[from];
			//翻转
			for (j = 1; j <= size[from]; j++)
			{
				if (body->isturned)
					body = body->pre;
				else
					body = body->next;
				pri->isturned = 1 - pri->isturned;
				pri = body;
			}
			//链接
			if (from_turned && !(to_turned)) //翻转对不翻转
			{
				head[from]->next = head[to];
				head[to]->pre = head[from];
			}
			else if (!(from_turned) && to_turned)//不翻转对翻转
			{
				head[from]->pre = head[to];
				head[to]->next = head[from];
			}
			else if (from_turned && to_turned)//翻转对翻转
			{
				head[from]->next = head[to];
				head[to]->next = head[from];
			}
			else //不翻转对不翻转
			{
				head[from]->pre = head[to];
				head[to]->pre = head[from];
			}
			//重新规定首尾
			head[to] = tail[from];
			head[from] = NULL;
			tail[from] = NULL;
			//数量改变
			size[to] += size[from];
			size[from] = 0;
		}
		else //从有到无
		{
			//翻转
			int from_turned = head[from]->isturned;
			node* body = head[from], * pri = head[from];
			for (j = 1; j <= size[from]; j++)
			{
				if (body->isturned)
					body = body->pre;
				else
					body = body->next;
				pri->isturned = 1 - pri->isturned;
				pri = body;
			}
			//空盒链接,head和tail互换
			tail[to] = head[from];
			head[to] = tail[from];
			head[from] = NULL;
			tail[from] = NULL;
			//数量改变
			size[to] += size[from];
			size[from] = 0;
		}
	}
	//遍历所有盒子,从head遍历到tail
	for (i = 1; i <= n; i++)
	{
		//遍历一个盒子
		node* body = head[i];
		if (body == NULL) //空盒子
			printf("0\n");
		else
		{
			printf("%d ", size[i]);
			for (j = 1; j <= size[i]; j++)
			{
				printf("%d%c", body->num, j == size[i] ? '\n' : ' ');
				if (body->isturned)
					body = body->pre;
				else
					body = body->next;
			}
		}
	}

	return 0;
}

 使用另一种思路的代码

TLE掉一个,临近上课了,补全的时间成本太大了,不打算补全了

先放舍友@Loyd. 的代码

#include <cstdio>  
#include <iostream>  
using namespace std;
typedef struct Node {
    Node* pre = NULL;
    Node* next = NULL;
    int value;
} node;
node list[100005];
node* head[100005];
node* tail[100005];
int a[100005];
int main(void)
{
    int n, m, x, y;
    node* pre;
    node* temp;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        temp = &(list[i]);
        temp->value = i;
        head[i] = temp;
        tail[i] = temp;
    }
    for (int i = 0; i < m; i++)
    {
        scanf("%d %d", &x, &y);
        if (head[x] == NULL)
        {
            continue;
        }
        if (head[x]->next == NULL)
        {
            head[x]->next = head[y];
        }
        else if (head[x]->pre == NULL)
        {
            head[x]->pre = head[y];
        }
        if (head[y] != NULL && head[y]->next == NULL)
        {
            head[y]->next = head[x];
        }
        else if (head[y] != NULL && head[y]->pre == NULL)
        {
            head[y]->pre = head[x];
        }
        else if (head[y] == NULL)
        {
            head[y] = tail[x];
            tail[y] = head[x];
        }
        head[y] = tail[x];
        head[x] = NULL;
        tail[x] = NULL;

    }
    for (int i = 1; i <= n; i++)
    {
        node* current = head[i];
        int cnt = 0;
        if (current == NULL)
        {
            printf("%d\n", cnt);
            continue;
        }
        else
        {
            a[cnt++] = current->value;
        }
        if (current->next != NULL)
        {
            pre = current;
            current = current->next;
            a[cnt++] = current->value;
        }
        else if (current->pre != NULL)
        {
            pre = current;
            current = current->pre;
            a[cnt++] = current->value;
        }
        else
        {
            printf("%d %d\n", cnt, current->value);
            continue;
        }
        while (current->next != NULL && current->pre != NULL)
        {
            if (current->next != pre)
            {
                pre = current;
                current = current->next;
                a[cnt++] = current->value;
            }
            else if (current->pre != pre)
            {
                pre = current;
                current = current->pre;
                a[cnt++] = current->value;
            }
        }
        printf("%d", cnt);
        for (int j = 0; j < cnt; j++)
        {
            printf(" %d", a[j]);
        }
        printf("\n");
    }
    return 0;
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦梦亦醒乐逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值