第一次训练赛题解
1.气球涂色问题
问题如下:
HDU1556
具体题意:
给定N个气球,然后给出N个区间,每次对区间上的气球刷一次色,最后输出每个气球涂了多少次色。
这个问题设计到的知识点是
差分,区间更新,线段树
在此回顾一下差分的相关概念:
差分:
即相邻两个数的差,用一个数组P存储数组A的差分,那么P是A的差分数组,即
P
i
=
A
i
−
A
i
−
1
P_i=A_i-A_{i-1}
Pi=Ai−Ai−1
此处要理解为当前项和前一项的差(因为复原改变后的数组时,需要考虑到第一项没有前一项的问题)
P[i]里存放着A[i]和A[i-1]的差,因为A[0]没有前一项,所以P[0]基本用不到。
用于对区间进行处理时有奇效(
现以本题为例,本题是区间加法的一个问题。
实现将数组A[l]到A[r]都加上K。
一般朴素做法(也就是我的做法)
如下:
for(int i=l;i<=k;i++)
{
a[i]=a[i]+k;
}
显然,当区间长度很长,操作次数很多的时候,这种做法时间复杂度会很高(很多段都重复操作了),会TLE(亲测会超时) 。
于是采用差分的方法。
现以对长度为10的数组A进行对区间[l,r]上的数加k的操作为例。
第一步:初始化差分数组
int P[10],A[10]= {0,1,2,3,90,5,6,7,8,9};
for(int i=1; i<10; i++)
{
P[i]=A[i]-A[i-1];
}
第二步:
令差分数组的第l项加k,第r+1项减k
P[l]+=k,P[r+1]-=k;
为什么这么写呢。
因为区间内的每个数字都增加相同的数,这时区间内的数的差值是不改变的,即差分数组的P[l+1]到P[r]不会改变。
但是相应的P[l]=A[l]-A[l-1]会增加K
P[r+1]=A[r+1]-A[r]会减少K
最后,如果需要得到操作后的数组,只需要运用:
for(int i=1;i<=9;i++)
{
A[i]=P[i]+A[i-1];
}
所以本题的题解:
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
int N,A,B;
cin>>N;
int Sign[100000]= {0};
int Data[100000]= {0};
while(N)
{
for(int i=0; i<N; i++)
{
cin>>A>>B;
Sign[A]++;
Sign[B+1]--;
}
for(int i=1; i<=N; i++) 这个地方从第一项开始,刚好题目要求也是从第
{ 一项开始,因为第0项没有前一项,所以复原的时候
Data[i]=Data[i-1]+Sign[i]; 会很麻烦。
cout<<Data[i]; 所以一般情况下会牺牲一个内存单元来换取对第0项
if(i!=N) 进行操作时的简便性。
cout <<" "
}
cout<<endl;
memset(Data,0,sizeof(Data));
memset(Sign,0,sizeof(Sign));
cin>>N;
}
return 0;
}
通过了解,在差分的基础上,这道题进一步可以有线段树的解法,日后再补充。其实是我现在还没学会
2.下象棋(广度优先搜索)
问题如下:
POJ1915
问题描述:
类似中国象棋里的马走“日”字,给出一个棋盘的大小,然后给出一个起始位置和一个终止位置,输出从起始位置走到终止位置的最小步数。
就是一个广度优先搜索问题,直接暴搜也可以过,但是训练当天脑子真的是太混了,前半段还在写着BFS,但是写着写着就成了DFS。。。考完之后复盘代码,哭笑不得,果然还是自己代码量太少了。
题解:
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
class Node
{
public:
int x,y;
int Times;
Node(int nx,int ny,int Tmptimes)
{
x=nx;
y=ny;
Times=Tmptimes;
}
};
int Sign[300][300]={0};
bool judge(int x,int y,int N)
{
if((x>=0)&&(x<N)&&(y>=0)&&(y<N)&&(Sign[x][y]!=1))
return true;
else
return false;
}
void BFS(int sx,int sy,int ex,int ey,int N) // 搜索起始位置和终止位置,棋盘的大小
{
queue<Node>A;
memset(Sign,0,sizeof(Sign));
int nx,ny;
A.push(Node(sx,sy,0));
while(!A.empty())
{
Node iter=A.front();
A.pop();
nx=iter.x,ny=iter.y;
if((nx==ex)&&(ny==ey))
{
cout<<iter.Times<<endl;
break;
}
if(judge(nx-1,ny-2,N)) //后面这几条搜索用两个数组存储然后循环
{ //操作更好,这样写八条确实有点蠢了
A.push(Node(nx-1,ny-2,iter.Times+1));
Sign[nx-1][ny-2]=1;
}
if(judge(nx-1,ny+2,N))
{
A.push(Node(nx-1,ny+2,iter.Times+1));
Sign[nx-1][ny+2]=1;
}
if(judge(nx-2,ny-1,N))
{
A.push(Node(nx-2,ny-1,iter.Times+1));
Sign[nx-2][ny-1]=1;
}
if(judge(nx-2,ny+1,N))
{
A.push(Node(nx-2,ny+1,iter.Times+1));
Sign[nx-2][ny+1]=1;
}
if(judge(nx+1,ny-2,N))
{
A.push(Node(nx+1,ny-2,iter.Times+1));
Sign[nx+1][ny-2]=1;
}
if(judge(nx+2,ny-1,N))
{
A.push(Node(nx+2,ny-1,iter.Times+1));
Sign[nx+2][ny-1]=1;
}
if(judge(nx+2,ny+1,N))
{
A.push(Node(nx+2,ny+1,iter.Times+1));
Sign[nx+2][ny+1]=1;
}
if(judge(nx+1,ny+2,N))
{
A.push(Node(nx+1,ny+2,iter.Times+1));
Sign[nx+1][ny+2]=1;
}
}
}
int main()
{
int N,Size,sx,sy,ex,ey;
cin>>N;
while(N)
{
cin>>Size>>sx>>sy>>ex>>ey;
BFS(sx,sy,ex,ey,Size);
N--;
}
return 0;
}
3.字符串操作(STL中Map和Sort的应用)
题目:
HDU1113
因为我本人STL掌握的并不是很好,这个题参考了其他大佬的代码才A掉,在这里就不写知识点了,放到之后的STL学习笔记中吧。其实就是懒。
题解:
#include<iostream>
#include<algorithm>
#include<map>
#include<string>
using namespace std;
int main()
{
string index,word;
map<string,string> dict;
while(cin>>word&&word!="XXXXXX")
{
index=word;
sort(index.begin(),index.end());
dict[word]=index;
}
while(cin>>word&&word!="XXXXXX")
{
bool flag=false;
sort(word.begin(),word.end());
for(map<string, string>::iterator it = dict.begin(); it != dict.end(); it++)
{
if(it->second==word)
{
cout<<it->first<<endl;
flag=true;
}
}
if(!flag)
cout<<"NOT A VALID WORD"<<endl;
cout<<"******"<<endl;
}
return 0;
}
4.道路维修问题(并查集)
题目:
HDU1232
题目大意:
给出M个城镇和N条道路,判断还需要修几条道路才能使所有城镇相连(间接相连也可以,例如1和2相连,2和3相连,则认为1和3间接相连)
题解:
没有用树形的结构存储,用了数组来存储。
只需要用并查集处理数据,最后算出集合数,然后减1输出即可。
但是,因为每次都要修改一个结点之前所有结点的根,所以时间复杂度会很高。相应的,还有树形存储结构和优化算法,在此不做详解。其实就是不会。
题解:
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int Data[1000]={0};
void Judge(int ,int);
int main()
{
int N,M;
cin>>N; // N是城镇数,M是修了的路数
while(N)
{
cin>>M;
Judge(N,M);
cin>>N;
}
return 0;
}
int Find(int x)
{
int i=x;
int j=x;
while(i!=Data[i]) //找到当前的这个数组的根
{
i=Data[i];
}
while(j!=i) // 将叶到根之间所有的点的根都改成同一个根
{
int Tmp=j;
j=Data[j];
Data[Tmp]=i;
}
return i;
}
void Judge(int N,int M) //
{
memset(Data,0,sizeof(Data));
for(int i=1;i<=N;i++)
{
Data[i]=i;
}
int A,B; // AB是路的两个端点
for(int j=0;j<M;j++)
{
cin>>A>>B;
int LocA=Find(A);
int LocB=Find(B);
if(LocA<LocB)
{
Data[LocB]=LocA;
}
else
{
Data[LocA]=LocB;
}
}
int Sum=0;
for(int i=1;i<=N;i++)
{
if(Data[i]==i)
Sum++;
}
cout<<Sum-1<<endl;
}
5. 我还妹看,在此给出链接留作以后填坑
题目:
POJ3714
6.我还妹看,在此给出链接留作以后填坑
题目:
Codeforce170C
总之,这次训练赛,打得很烂,让我充分地认识到自己眼高手低的事实,同时以前学过的知识没有做题加以巩固,所以不能熟练运用。代码量太少了以至于写起来代码很生疏,甚至不会写,以后会加以改正。