本周练习主要涉及基本数据结构、树的直径、LCA入门、倍增、树状数组、RMQ、差分
已完成题目 POJ: 3253、3264、2492、2421;HDU:1251、1213、CodeForces 1467A、977D、LibreOJ 2421
未完成题目 POJ: 1330、2631、1125、1195、2352、2299、3368;HDU:1394、4825、3183;LightOJ 1113、计蒜客A1633、CodeForces: 1000C、1467B、920E、1473B
POJ 3253
题目大意:给出N块木板,每合并两块木板所需要的花费为两木板长度之和,求出合并完成时的最小花费
思路:哈夫曼树的变形用法,每两次选取最小的两块板合并成一块新的板,累和,循环操作(如果取的不是最小值,花费必定不为最小)
代码
#include
#include
using namespace std;
priority_queue<int,vector<int>,greater<int>>Q;
int N,L;
long long ans;
int main()
{
cin >>N;
for(int i=0;i<N;i++)//数据录入
{
cin >>L;
Q.push(L);
}
while(Q.size()!=1)//最后一个数据不需要
{
int a=Q.top();
Q.pop();
int b=Q.top();
Q.pop();
ans+=a+b;//累和
Q.push(a+b);
}
cout <<ans;
return 0;
}
POJ 3264
题目大意:给出一个乱序序列,每次给出一个范围L~R的询问,求出该范围内最大值与最小值的差
思路:RMQ问题,采用线段树的模型来解决,构造两棵树
代码
#include
#include
#include
using namespace std;
int N,Q,A,B,cowMax[212121],cowMin[212121],n=1;
void update(int k,int x)//更新操作,构造线段树
{
k+=n-1;//从叶子节点开始建树,n必须是2的整数次幂,类似堆的插入
cowMax[k]=x;
cowMin[k]=x;
while(k)
{
k>>=1;
cowMax[k]=max(cowMax[2*k],cowMax[2*k+1]);
cowMin[k]=min(cowMin[2*k],cowMin[2*k+1]);
}
}
int queryMax(int k,int l,int r)//查询区域最大值
{
if(r<A||B<l)return 0;//如果查询区域与执行区域不相交,返回预设值,不能取等号,否则等号的值可能会在递归中无法取到
if(A<=l&&r<=B)return cowMax[k];//如果查询区域完全包含执行区域,返回执行区域最值
return max(queryMax(k*2,l,(l+r)/2),queryMax(k*2+1,(l+r)/2+1,r));//否则取左执行区域与右执行区域比较后最值
}
int queryMin(int k,int l,int r)//查询区域最小值
{
if(r<A||B<l)return INT_MAX;
if(A<=l&&r<=B)return cowMin[k];
return min(queryMin(k*2,l,(l+r)/2),queryMin(k*2+1,(l+r)/2+1,r));
}
int main()
{
scanf("%d%d",&N,&Q);
while(n<N)
n<<=1;
for(int i=1; i<=2*n; i++)
cowMin[i]=INT_MAX;
for(int i=1; i<=N; i++)
{
int x;
scanf("%d",&x);
update(i,x);
}
while(Q--)
{
scanf("%d%d",&A,&B);
if(A!=B)
printf("%d\n",queryMax(1,1,n)-queryMin(1,1,n));
else
printf("0\n");
}
return 0;
}
POJ 2492
题目大意:有N只虫子,每个个体只有雌和雄,给出数对性别相异的虫子对,判断是否有矛盾(同性配对)
思路:带权并查集,在一般的并查集基础上加上了相反关系,对于一个虫子(编号为x),假设一个它的对立性别的虫子编号为x+N,于是该问题被转换成简单的并查集问题
代码
#include
#include
using namespace std;
const int maxn = 1e6 + 7;
int f[20007];
int Find(int x){
if (f[x] == x) return x;
return f[x] = Find(f[x]);
}//路径压缩+寻找祖先
void mix(int u, int v){
int x = Find(u), y = Find(v);
if (x == y) return;
f[x] = y;
}//合并
int main(){
int t; scanf("%d", &t);
int cas = 0;
while (t --){
int n, m; scanf("%d%d", &n, &m);
for (int i=0; i<=2 * n; i++) f[i] = i;
bool flag = false;//标记是否有异常
for (int i=1; i<=m; i++){
int u, v; scanf("%d%d", &u, &v);
if (flag) continue;
int x = Find(u), y = Find(v);//查找祖先(即判断是否同性)
if (x == y) {
flag = true; continue;
}else{
mix(u, v + n);//合并对应性别
mix(v, u + n);
}
}
printf("Scenario #%d:\n", ++cas);
if (flag) printf("Suspicious bugs found!\n\n");
else printf("No suspicious bugs found!\n\n");
}
return 0;
}
HDU 1251
题目大意:略
思路:map/unordered_map的简单应用,存储以每种前缀在词典中出现的次数,根据每次查询直接取值即可
代码
#include
#include
#include
#include
using namespace std;
unordered_map<string,int>UMap;
char str[12];
int main()
{
while(gets(str))
{
int len=strlen(str);
if(!len)
break;
string s;
for(int i=0;i<len;i++)
{
s+=str[i];
UMap[s]++;
}
}
string t;
while(cin >>t)
cout <<UMap[t]<<endl;
return 0;
}
POJ 2421
题目大意:有N个村庄,每个村庄有一定的距离,现在要修路使得总距离和最小,并且有些村庄已经修好了路
思路:最小生成树问题,去掉已经生成的边即可
代码
#include
#include
#include
#include
using namespace std;
int Pre[12121],N,Q;
bool if_in[121][121];
typedef struct edge
{
int a,b,value;
edge(int _a,int _b,int dis)
{
a=_a;
b=_b;
value=dis;
}
bool operator <(edge x)const
{
return value>x.value;
}
} edge;
int Seek(int x)
{
if(Pre[x]==x)return x;
return Pre[x]=Seek(Pre[x]);
}
bool Union(int a,int b)
{
int fa=Seek(a),fb=Seek(b);
if(fa==fb)
return false;
Pre[fa]=fb;
return true;
}
priority_queue<edge>Que;
int main()
{
cin >>N;
for(int i=1; i<=N; i++)
{
Pre[i]=i;
for(int j=1; j<=N; j++)
{
int dis;
cin >>dis;
if(i==j||if_in[i][j]||if_in[j][i])
continue;
if_in[i][j]=true;
if_in[j][j]=true;
Que.push(edge(i,j,dis));
}
}
cin >>Q;
while(Q--)
{
int a,b;
cin >>a>>b;
int fa=Seek(a),fb=Seek(b);
if(fa!=fb)
Pre[fa]=fb;
}
int ans=0,sum=0;
while(!Que.empty()&&ans!=N-1)
{
edge tmp=Que.top();
Que.pop();
if(Union(tmp.a,tmp.b))
{
sum+=tmp.value;
ans++;
}
}
cout <<sum;
return 0;
}
HDU 1213
题目大意:给出有N个人,两两之间有朋友,朋友的朋友为朋友,给出M对朋友,问有几个朋友群
思路:并查集
代码
#include
#include
using namespace std;
int pre[12121],T,N,M;
int Seek(int x)
{
if(pre[x]==x)
return x;
return pre[x]=Seek(pre[x]);
}
void Union(int a,int b)
{
int fa=Seek(a),fb=Seek(b);
pre[fa]=fb;
}
int main()
{
cin >>T;
while(T--)
{
cin >>N>>M;
for(int i=1; i<=N; i++)
pre[i]=i;
while(M--)
{
int a,b;
cin >>a>>b;
Union(a,b);
}
int ans=0;
for(int i=1; i<=N; i++)
if(pre[i]==i)
ans++;
cout <<ans<<endl;
}
return 0;
}
CodeForces 1467A
题目大意:给出一块电子屏幕,电子屏幕有给定的n个数位,一开始这些数位都为0,每过一秒自增到达10便循环,现在立刻停下某一位,而其相邻位需要隔一秒才能停下,以此类推,相邻的相邻需要两秒…求出完全停下时显示的最大值
思路:为确保最大,首位要确保最大,那么只需在第8秒时停下第二位即可,整个数最大
代码
#include
using namespace std;
int T,N;
int main()
{
cin >>T;
while(T--)
{
cin >>N;
if(N==1)
cout <<"9";
else if(N==2)
cout <<"98";
else
{
cout <<"989";
N-=3;
int i=9;
while(N--)
cout <<(++i)%10;
}
cout <<endl;
}
return 0;
}
LibreOJ 2421
题目大意:略
思路:DFS,对每个编号进行DFS,判断回到出发点需要几步,选取最少步数
#include
#include
#include
using namespace std;
int object[212121],n,level,visit[212121],ans=212121;
int DFS(int x)
{
int tmp=level+1;
while(!visit[x])//以一个方向进行延伸
{
visit[x]=++level;//visit记录多少层,这个层数相对于当前的函数的参数x
x=object[x];
}
if(tmp>visit[x])
return 212121;
return level-visit[x]+1;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&object[i]);
for(int i=1; i<=n; i++)
if(!visit[i])ans=min(ans,DFS(i));
cout <<ans<<endl;
return 0;
}
CodeForces 977D
题目大意:给出n个数(乱序),对于其中任意一个数X,总能找到另一个数Y(除了首个数),使得X×2 = = == ==Y或者X%3 = = == == 0&&X/3 = = == == Y,求出符合这个关系的序列
思路:DFS,不过是从两个方向进行搜索
代码
#include
#include
#include
#include
using namespace std;
typedef long long ll;
bool visited[121],finish;
ll N,data[121],ans;
deque<ll>Q;
void DFS(ll x)
{
bool flag=false;
if(ans==N-1)
{
Q.push_back(data[x]);
while(Q.size()!=1)
{
printf("%lld ",Q.front());
Q.pop_front();
}
printf("%lld",Q.front());
Q.pop_back();
finish=true;
return;
}
for(int i=1; i<=N; i++)
{
if(finish)
return;
if(data[x]%3==0)
{
if(!visited[i]&&data[x]/3==data[i])
{
visited[i]=true;
ans++;
flag=true;
Q.push_back(data[x]);
DFS(i);
visited[i]=false;
ans--;
Q.pop_back();
}
}
if(!visited[i]&&data[x]*2==data[i])
{
visited[i]=true;
ans++;
if(!flag)
Q.push_back(data[x]);
DFS(i);
visited[i]=false;
ans--;
if(!flag)
Q.pop_back();
}
flag=false;
}
}
int main()
{
scanf("%lld",&N);
for(int i=1; i<=N; i++)
scanf("%lld",&data[i]);
for(int i=1; i<=N; i++)
if(!finish)
DFS(i);
return 0;
}
完成量相对于总任务量还是不够的,主要是前期的各个知识点掌握不牢,训练也不够,准备巩固一遍上学期队里讲过的知识点,应该对眼下的境况有所帮助,未完成的题目在此记下。