P1219 [USACO1.5] 八皇后 Checker Challenge
可以说是一道对目前的我来说难度比较高的一道题,题目要求得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子,这就需要去找规律了。对于第i行j列的数寻找规律,可以发现同列的j都是一样的,同逆对角线,i+j的值是相同的,同顺对角线i-j的值是相同的,可以用3个一维数组记录是否被选取的状态,主义i-j可能会越界,在记录的时候加上一个题目给出的n就好了,用到了dfs的方法,记住在每一次选择完成后进行回溯。
#include<bits/stdc++.h>
using namespace std;
int n,m1[30],m2[30],m3[30],ans[18],cnt;
void setvalue(int x,int y,int k){
ans[x]=y;
m1[y]=k;
m2[x+y]=k;
m3[x-y+n]=k;
}
void dfs(int step){
if(step>n){
cnt++;
if(cnt<=3){
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
printf("\n");
}
return;
}
for(int j=1;j<=n;j++){
if(m1[j]||m2[step+j]||m3[step-j+n])continue;
setvalue(step,j,1);
dfs(step+1);
setvalue(step,j,0);
}
}
int main(){
cin>>n;
dfs(1);
cout<<cnt;
return 0;
}
P2058 [NOIP2016 普及组] 海港
线性表题单里的一道题,
首先看到这里,我们首先的思路是开个结构体。
从第一个船开始遍历找到这个船24小时内进港的船,再统计国籍数,输出,进行下一波遍历。
那么问题就来了,我们如果记录每个状态可能会爆内存。(算内存应该都会吧)我们能否对这个算法进行优化呢???
-
优化①: 在读入时我们即可记录下每个船的人的国籍(去重),这样只要记下人头数就行了(无需记船了)。
-
优化②: 将每艘船上的人的到达时间和国籍记录下来,把结构体放进一个队列中。由于是比较之前的,所以边读一次输出一次就免得重新遍历了。
但是细细一想。这群人来的批数有用吗???我们也许只要开个结构体存人头数(这个人来的时间和这个人的所属国籍)不就行了嘛,那么问题就很好办了。
我们的思路是:先读进人数,再读进每个人的国籍,把人到来的时间和国籍放进队列。去重计数(因为对答案没贡献)。
其次,队列并不要记录每个结构体,它只要记住主时间线之前24小时内的结构体即可。
所以我们先全记下来,再判重,之后再进行队列和答案的更新,最后输出答案即可(答案其实不用清零,因为之前的答案有可能会对现在的做贡献,这样就不用每次读入都遍历了)
#include<bits/stdc++.h>
using namespace std;
struct nb{
int s;
int time;
};
queue<nb>ship;
nb h;
int n,t,m,x,cnt,temp[1000008];
int main(){
scanf("%d",&n);
while(n--){
scanf("%d %d",&t,&m);
while(!ship.empty()){
h=ship.front();
if(h.time+86400<=t){
temp[h.s]--;
if(temp[h.s]==0)cnt--;
ship.pop();
continue;
}
break;
}
while(m--){
scanf("%d",&x);
h.s=x,h.time=t;
ship.push(h);
temp[x]++;
if(temp[x]==1)cnt++;
}
printf("%d\n",cnt);
}
return 0;
}
P1241 括号序列
一道比较难以理解的题目,一开始写了个56分的题解,一直以为是自己代码的问题,奇怪的是样例也也看不了,看了一下讨论区很多警示后人的帖子才真正读懂题目,本题需要用到栈对平衡状态进行标记,就是从头搜,如果是左括号就入栈,先假设它们都需要匹配,把相应的右括号存进b数组;如果是右括号就看栈顶端的左括号是否与之匹配,匹配则将b数组中存的右括号清除,不匹配则添加相应左括号。输出时先看一下b数组补全左括号再输出,然后再看一遍b数组补全右括号。
#include<bits/stdc++.h>
using namespace std;
int mark[111];
stack<int>sta;
int main(){
char str[111];
scanf("%s",str);
int len=strlen(str);
for(int i=0;i<len;i++){
if(str[i]=='('||str[i]=='['){
sta.push(i);
}
else if(str[i]==']'){
if(sta.empty()){
mark[i]=1;
}
else{
if(str[sta.top()]!='[')mark[i]=1;
else sta.pop();
}
}
else if(str[i]==')'){
if(sta.empty()){
mark[i]=1;
}
else{
if(str[sta.top()]!='(')mark[i]=1;
else sta.pop();
}
}
}
while(!sta.empty()){
mark[sta.top()]=1;
sta.pop();
}
for(int i=0;i<len;i++){
if(mark[i]==0){
printf("%c",str[i]);
}
else if(mark[i]!=0){
if(str[i]=='('||str[i]==')'){
printf("()");
}
else{
printf("[]");
}
}
}
return 0;
}
数据结构的学习
了解了双端链表
双端链表与单链表相比,具有一个明显的优势,即它可以从两个方向(前向和后向)遍历链表。以下是双端链表的一些优势:
-
前向和后向遍历: 双端链表允许从链表的两端开始遍历。这在某些情况下可以提供更灵活的遍历方式,尤其是当需要从尾部向前遍历链表时。
-
插入和删除操作更高效: 对于双端链表,在已知节点的情况下,插入或删除该节点后继节点的操作更为高效。因为双端链表中的节点通常包含指向前一个节点和后一个节点的指针,所以可以直接通过修改前一个节点的后继指针或后一个节点的前驱指针来实现插入或删除操作。
-
方便处理边界情况: 在某些情况下,双端链表更容易处理边界情况。例如,在需要在链表头和链表尾之间执行操作时,双端链表提供了更直观和高效的方式。
-
更灵活的操作: 双端链表使得一些特定场景下的操作更加灵活,例如在某个节点之前或之后插入新节点,或者在已知节点的情况下删除它。
尽管双端链表有这些优势,但它也有一些缺点,比如更复杂的实现和更多的内存占用。在选择数据结构时,需要根据具体的使用场景和需求来决定是否使用双端链表。
#include<stdio.h>
#include<stdlib.h>
struct dnode{
int date;
dnode *front;
dnode *next;
};
int main(){
dnode *head=NULL,*news=NULL,*move=NULL,*end=NULL;
int temp;
while(~scanf("%d",&temp)){
news=(struct dnode*)malloc(sizeof(struct dnode));
news->date=temp;
move=head;
if(head==NULL){
head=news;
news->front=NULL;
news->next=NULL;
}
else{
while(move->next!=NULL)move=move->next;
move->next=news;
news->front=move;
news->next=NULL;
end=news;
}
}
//利用双链表进行逆序输出
move=end;
while(move!=NULL){
printf("%d ",move->date);
move=move->front;
}
move=head;
while(move!=NULL){
news=move;
move=move->next;
free(news);
}
return 0;
}
接下来便是双端链表的插入,分为三种,头部插入,中间插入,尾部插入,头部插入和尾部插入形式类似,而中间插入则有四步操作,更换四个指针,要注意指针在更换时此时所指的位置,不然四个指针位置转换的顺序很可能会出错,导致结果错误
下面是我自己用已知顺序的链表进行插入操作,然后逆序进行输出
#include<stdio.h>
#include<stdlib.h>
struct dnode{
int date;
dnode *front;
dnode *next;
};
int main(){
dnode *head=NULL,*news=NULL,*move=NULL,*end=NULL;
int temp,n;
scanf("%d",&n);
while(n--){
scanf("%d",&temp);
news=(struct dnode*)malloc(sizeof(struct dnode));
news->date=temp;
move=head;
if(head==NULL){
head=news;
news->front=NULL;
news->next=NULL;
}
else{
while(move->next!=NULL)move=move->next;
move->next=news;
news->front=move;
news->next=NULL;
end=news;
}
}
printf("请输入你要插入的数字\n");
scanf("%d",&temp);
news=(struct dnode*)malloc(sizeof(struct dnode));
news->date=temp;
if(temp>end->date){
end->next=news;
news->front=end;
news->next=NULL;
end=news;
}
else if(temp<head->date){
news->next=head;
head->front=news;
news->front=NULL;
head=news;
}
else{
move=head;
while(temp>move->date&&move!=NULL){
move=move->next;
}
news->front=move->front;
move->front->next=news;
news->next=move;
move->front=news;
}
//利用双链表进行逆序输出
move=end;
while(move!=NULL){
printf("%d ",move->date);
move=move->front;
}
move=head;
while(move!=NULL){
news=move;
move=move->next;
free(news);
}
return 0;
}
这里如果用多组输入的方法我就不会怎么输入插入的数字了,所以在输入原链表时要先已知链表的成员个数。