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;
}