指针和链表

1、UVA11988破损的键盘


题意翻译


你在输入文章的时候,键盘上的Home键和End键出了问题,会不定时的按下。你却不知道此问题,而是专心致志地打稿子,甚至显示器都没开。当你打开显示器之后,展现你面前的数一段悲剧文本。你的任务是在显示器打开前计算出这段悲剧的文本。 给你一段按键的文本,其中'['表示Home键,']'表示End键,输入结束标志是文件结束符(EOF)。

输出一行,即这段悲剧文本。 翻译贡献者UID:71371

输入输出样例
输入 
This_is_a_[Beiju]_text
[[]][][]Happy_Birthday_to_Tsinghua_University
输出 
BeijuThis_is_a__text
Happy_Birthday_to_Tsinghua_University

#include 
using namespace std;
struct node
{    char val;
    node *next;
} *head, *tail, *now;
int main ()
{   char s[100086];
    while (scanf("%s", s) != EOF)
    {   head = tail = now = new node;
        head->next = tail->next = now->next = NULL;
        int str = strlen(s);
        for (int i = 0; i < str; i++)
        {    if (s[i] == '[') now = head;
            else if (s[i] == ']') now = tail;
                else
                  {   node *tmp = new node;
                    tmp->val = s[i], tmp->next = now->next;
                    now->next = tmp;
                    now = tmp;
                    if (now->next == NULL)tail = now;
                }
        }
        for (now = head->next; now != NULL; now = now->next)
            printf("%c", now->val);
        puts("");
    }
    return 0;
}

2、UVA12657 移动盒子

题意翻译

你有n个盒子在桌子上的一条线上从左到右编号为1……n。你的任务是模拟四种操作

1 X Y 移动盒子编号X到盒子编号Y的左边(如果X已经在Y的左边了就忽略)

2 X Y 移动盒子编号X到盒子编号Y的右边(如果X已经在Y的右边了就忽略)

3 X Y 交换盒子编号X与盒子编号Y的位置

4 将整条线反转

操作保证合法,X不等于Y

举一个例子,如果n=6,操作 1 1 4然后就变成了2 3 1 4 5 6;再操作 2 3 5就变成了 2 1 4 5 3 6;再操作 3 1 6 就变成 2 6 4 5 3 1;最后操作4,就变成了 1 3 5 4 6 2

输入

最多有10组数据,每个数据会包含两个整数n,m(1≤n,m<100,000), 接下来是m行数据,表示操作。

输出

对于每组数据,输出他们奇数位置的编号的和。

输入输出样例

输入 

6 4
1 1 4
2 3 5
3 1 6
4
6 3
1 1 4
2 3 5
3 1 6
100000 1
4

输出 

Case 1: 12
Case 2: 9
Case 3: 2500050000

用链表实现

#include 
using namespace std;
struct node
{	int data;
	node *next;
};
int n,m,id;
node *zhi[100005];
bool flag;
int main()
{	while(scanf("%d%d",&n,&m)!=EOF)
	{	++id;//第几组数据,输出时用 
		flag=1;//顺序正序,0则为倒序 
		node *head=new node;//新的head 
		head->next=NULL;//初值 
		node *ptr=head;//记录上一个指针 
		for(int i=1;i<=n;++i)
		{	node *p=new node;//新的指针 
			p->next=NULL;//目前下一个为空 
			p->data=i;//数为i 
			ptr->next=p;//上一个数的next指向p 
			zhi[i]=ptr;//这个数的上一个数的指针为ptr 
			ptr=p;//更新ptr 
		}
		for(int i=1;i<=m;++i)
		{	int k,x,y;
			scanf("%d",&k);
			if(!flag&&(k==1||k==2))//如果倒序又是与左右有关系 
				k=3-k;//交换 
			if(k==1)
			{	scanf("%d%d",&x,&y);
				node *p=zhi[x]->next;
				zhi[x]->next=zhi[x]->next->next;
				if(zhi[x]->next)
					zhi[zhi[x]->next->data]=zhi[x];
				/*删掉x的位置*/ 
				p->next=zhi[y]->next;
				zhi[y]->next=p;
				zhi[x]=zhi[y];
				zhi[p->next->data]=p;
				/*把x放到y的左边*/
			}
			else if(k==2)
			{
				scanf("%d%d",&x,&y);
				node *p=zhi[x]->next;
				zhi[x]->next=zhi[x]->next->next;
				if(zhi[x]->next)
					zhi[zhi[x]->next->data]=zhi[x];
				/*删掉x的位置*/ 
				p->next=zhi[y]->next->next;
				zhi[y]->next->next=p;
				zhi[x]=zhi[y]->next;
				if(p->next)
					zhi[p->next->data]=p;
				/*把x放到y的右边*/ 
			}
			else if(k==3)
			{	scanf("%d%d",&x,&y);
				if(zhi[y]->next==zhi[x])//x是y的下一个,方便起见交换一下,反正没有顺序 
					swap(x,y);
				if(zhi[x]->next==zhi[y])//相邻 
				{	node *p=zhi[x]->next,*q=zhi[y]->next;
					zhi[x]->next=q;
					p->next=q->next;
					q->next=p;
					zhi[y]=zhi[x];
					zhi[x]=q;
					if(p->next)
						zhi[p->next->data]=p;
				}
				else//不相邻 
				{	node *p=zhi[x]->next,*q=zhi[y]->next,*p1=p->next,*q1=q->next;				
					zhi[x]->next=q,q->next=p1;
					zhi[y]->next=p,p->next=q1;;
					node *t=zhi[y];
					zhi[y]=zhi[x];
					zhi[x]=t;
					if(p1) zhi[p1->data]=q;
					if(q1) zhi[q1->data]=p;
				}
			}
			else//反转 
				flag=!flag;
		}
		bool ji=0;
		long long sum=0;//记得开long long哦 
		ptr=head;
		for(node *i=head->next;i;i=i->next)
		{
			ji=!ji;
			if(n%2&&ji)//n为奇数,第奇数个 
                    sum+=i->data;
            else if(!(n%2)&&flag&&ji)//n为偶数,第奇数个,反转偶数次 
                    sum+=i->data;
            else if(!(n%2)&&!flag&&!ji)//n为偶数,第偶数个,反转奇数次 
                    sum+=i->data;
            delete ptr;//防止内存泄漏,删了! 
            ptr=i;//更新ptr 
		}
		delete ptr;//最后一个还没删,删了! 
		printf("Case %d: %lld\n",id,sum);//输出 
	}
	return 0;
}

用数组模拟

#include 
using namespace std;
int ri[100005], le[100005];
int link(int l, int r)
{	ri[l] = r;
	le[r] = l;
} 
int main() 
{	int n, m, cases = 1;
	while(scanf("%d%d", &n, &m) != EOF)
	{	int i, sta = 0;
		ri[n] = 0;
		le[0] = n;
		for(i = 0; i < n; i++)	link(i, i+1);
		for(i = 1; i <= m; i++)
		{	int p, x, y;
			scanf("%d", &p);
			if(p == 4) sta = !sta;
			else
			{	scanf("%d%d", &x, &y);
				if(p == 3 && ri[y] == x) swap(x, y);
				if(sta && (p == 1 || p == 2)) p = 3 - p;
				if(p == 1 && ri[x] == y) continue;
				if(p == 2 && ri[y] == x) continue;
			}
			int lx = le[x], rx = ri[x], ly = le[y], ry = ri[y];
			switch (p) 
			{	case 1 :
			    {	link(lx, rx);
					link(ly, x);
					link(x, y);
					break;
				}
				case 2 :
				{	link(lx, rx);
					link(x, ry);
					link(y, x);
					break;
				}
				case 3 : 
				{	link(lx, y);
					link(x, ry);
					if(rx == y) link(y, x);
					else{link(y, rx);link(ly, x);} 
					break;
				}
				default : break;
			}
		}
		long long ans = 0;
		int b = 0;
		for(i = 1; i <= n; i++)
		{	b = ri[b];
			if(i % 2 == 1) ans += b;
		}	
		if(sta && n % 2 == 0) ans = (long long)n * (n + 1) / 2 - ans;
		printf("Case %d: %lld\n", cases++, ans);
	}
	return 0;
}

3、P7912 [CSP-J 2021] 小熊的果篮

题目描述

小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1,2,…,n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里,具体方法是:每次都把每一个“块”中最左边的水果同时挑出,组成一个果篮。重复这一操作,直至水果用完。注意,每次挑完一个果篮后,“块”可能会发生变化。比如两个苹果“块”之间的唯一桔子被挑走后,两个苹果“块”就变成了一个“块”。请帮小熊计算每个果篮里包含的水果。

输入格式

第一行,包含一个正整数 n,表示水果的数量。

第二行,包含 n 个空格分隔的整数,其中第 i 个数表示编号为 i 的水果的种类,1 代表苹果,0 代表桔子。

输出格式

输出若干行。

第 i 行表示第 i 次挑出的水果组成的果篮。从小到大排序输出该果篮中所有水果的编号,每两个编号之间用一个空格分隔。

输入输出样例

输入 

12
1 1 0 0 1 1 1 0 1 1 0 0

输出 

1 3 5 8 9 11
2 4 6 12
7
10

输入 

20
1 1 1 1 0 0 0 1 1 1 0 0 1 0 1 1 0 0 0 0

输出 

1 5 8 11 13 14 15 17
2 6 9 12 16 18
3 7 10 19
4 20

输入 

见附件中的 fruit/fruit3.in。

输出 

见附件中的 fruit/fruit3.ans。

说明/提示

【样例解释 #1】

这是第一组数据的样例说明。

所有水果一开始的情况是 [1,1,0,0,1,1,1,0,1,1,0,0],一共有 6 个块。

在第一次挑水果组成果篮的过程中,编号为 1,3,5,8,9,11 的水果被挑了出来。

之后剩下的水果是 [1,0,1,1,1,0],一共 4 个块。

在第二次挑水果组成果篮的过程中,编号为2,4,6,12 的水果被挑了出来。

之后剩下的水果是 [1,1],只有 1 个块。

在第三次挑水果组成果篮的过程中,编号为 7 的水果被挑了出来。

最后剩下的水果是 [1],只有 1个块。

在第四次挑水果组成果篮的过程中,编号为 10 的水果被挑了出来。

【数据范围】

对于 10% 的数据,n≤5。
对于 30% 的数据,n≤1000。
对于 70% 的数据,n≤50000。
对于 100% 的数据,1≤n≤2×10^5。

【提示】

由于数据规模较大,建议 C/C++ 选手使用 scanf 和 printf 语句输入、输出。

#include 
#define MAXN 200100
using namespace std;
int n, a[MAXN], l[MAXN], r[MAXN];
vector b;

int main() 
{ scanf("%d", &n);
  a[0] = a[n + 1] = -1, r[0] = 1, l[n + 1] = n;
  for (int i = 1; i <= n; i++) {
      scanf("%d", &a[i]);
      if (a[i] != a[i - 1]) b.push_back(i);
      l[i] = i - 1, r[i] = i + 1;
  }
  while (r[0] != n + 1) {
      vector tmp;
      for (int i = 0; i < b.size(); i++) {
          printf("%d ", b[i]);
          int u = l[b[i]], v = r[b[i]];
          r[u] = v, l[v] = u;
          if (a[b[i]] != a[u] && a[b[i]] == a[v]) tmp.push_back(v); 
      }
      puts("");
      b = tmp;
  }
  return 0;
}

你可能感兴趣的:(数据结构,数据结构)