歇息了一天~今天把前两天的一起补起来:
第十九次:只能85-93封顶,第二题死死卡住时间复杂度
题目 | 难度 | 知识点 |
---|---|---|
1128 N Queens Puzzle | 🎯 | N皇后? |
1129 Recommendation System | 🎯🎯|🎃 | set的另类用法 |
1130 Infix Expression | 🎯 | 语法树 |
1131 Subway Map | 🎯🎯|🎃 | 图论 |
第二十次:比较简单,没有卡壳的几乎
题目 | 难度 | 知识点 |
---|---|---|
1124 Raffle for Weibo Followers | 🎯 | 数组 |
1125 Chain the Ropes | 🎯 | 贪心+找规律 |
1126 Eulerian Path | 🎯 | 图论(欧拉图的概念) |
1127 ZigZagging on a Tree | 🎯 | 二叉树的构建和层次遍历 |
#include
using namespace std;
int K,N,c,r;
vector<int>nodes;
vector<bool>vis;
int main(){
scanf("%d",&K);
while(K--){
scanf("%d",&N);
nodes.resize(N+1);
vis.resize(N+1);
fill(vis.begin(),vis.end(),false);
bool flag1=true;
for(int r=1;r<=N;r++){
scanf("%d",&nodes[r]);
if(vis[nodes[r]]){
flag1=false;
}
vis[nodes[r]]=true;
}
//首先查找有无同列
if(!flag1){
printf("NO\n");
continue;
}
for(int i=1;i<=N;i++){
//其次查找有无同斜线
for(int j=i+1;j<=N;j++){
if(nodes[i]-i==nodes[j]-j){
//printf("(%d,%d)(%d,%d)\n",i,nodes[i],j,nodes[j]);
flag1=false;
break;
}
}
if(!flag1){
printf("NO\n");
break;
}
}
if(flag1){
printf("YES\n");
}
}
return 0;
}
一开始采用的是优先队列,卡了倒数2、3测试点:
#include
using namespace std;
int N,K;
//优先队列:超时18/25
struct Node{
int id;
int time=0;
bool operator <(const Node node)const{
if(this->time!=node.time){
return this->time<node.time;
}else{
return this->id>node.id;
}
}
};
priority_queue<Node>q;
vector<int> ids;
Node temp;
void Push(int id){
//将元素导入优先队列
vector<Node>temp;
Node top;
while(!q.empty()){
top=q.top();
q.pop();
if(top.id!=id){
temp.push_back(top);
}else{
top.time++;
temp.push_back(top);
break;
}
}
for(int i=0;i<temp.size();i++){
q.push(temp[i]);
}
temp.clear();
}
void Pop(int nid){
//吐出前K个
vector<Node>temp;
Node top;
int cnt=0;
while(!q.empty()) {
cnt++;
top=q.top();
if(top.time==0||cnt>K){
break;
}
temp.push_back(top);
q.pop();
}
printf("%d:",nid);
for(int i=0;i<temp.size();i++){
printf(" %d",temp[i].id);
q.push(temp[i]);
}
printf("\n");
temp.clear();
}
int main(){
scanf("%d%d",&N,&K);
//初始化
ids.resize(N+1);
for(int i=1;i<=N;i++){
scanf("%d",&ids[i]);
temp.id=i;
q.push(temp);
}
for(int i=1;i<N;i++){
Push(ids[i]);
Pop(ids[i+1]);
}
return 0;
}
优先队列最大的问题在于取数困难,浪费了大量的时间
后来考虑队列的解法还是不太好,依旧是倒数3、4测试点过不去
#include
using namespace std;
int N,K,temp;
struct Node{
int id;
int times=0;
Node*next=NULL;
};
Node*build(){
//创建一个带头节点的单链表
Node*head,*p,*pre;
head=new Node;
pre=head;
for(int i=1;i<=N;i++){
p=new Node;
p->id=i;
pre->next=p;
pre=p;
}
return head;
}
void Insert(int id,Node*head){
Node*pre,*p,*np,*npre;
pre=head;
p=head->next;
while(p!=NULL){
if(p->id==id){
p->times++;
break;
}
pre=p;
p=p->next;
}
//重新选位置
pre->next=p->next;
bool flag=false;
np=head;
npre=head;
while(np!=NULL){
if(np->next!=NULL){
if(np->next->times<p->times||(np->next->times==p->times&&np->next->id>p->id)){
// printf("和%d换位置\n",np->id);
p->next=np->next;
np->next=p;
flag=true;
break;
}
}
npre=np;
np=np->next;
}
if(!flag){
npre->next=p;
p->next=NULL;
}
}
void Print(Node*head){
Node*p;
//printf("print\n");
p=head->next;
int cnt=0;
while(p!=NULL){
if(p->times==0){
break;
}
cnt++;
if(cnt>K){
break;
}else{
printf(" %d",p->id);
}
p=p->next;
}
printf("\n");
}
void show( Node*head){
Node*p,*pre=head;
printf("show:\n");
p=head->next;
while(p!=NULL){
printf("%d:%d\n",p->id,p->times);
p=p->next;
}
head=pre;
printf("\n");
}
int main(){
scanf("%d%d",&N,&K);
Node*head=build();
scanf("%d",&temp);
Insert(temp,head);
//show(head);
for(int i=1;i<N;i++){
scanf("%d",&temp);
printf("%d:",temp);
Print(head);
Insert(temp,head);
//show(head);
}
return 0;
}
最后参考柳神的代码,成功AC:
第一次知道set的时间复杂度小于前面两个……
#include
using namespace std;
int N,K,volum=0;
vector<int>v;
vector<int>cnt(50001,0);//记录次数
struct Node{
int id;
int times;
Node(int a,int b):id(a),times(b){
};//构造函数
bool operator <(const Node&a)const{
//重载<符号
return (times!=a.times)?times>a.times:id<a.id;
}
};
set<Node>st;
int main(){
scanf("%d%d",&N,&K);
v.resize(N);
for(int i=0;i<N;i++){
scanf("%d",&v[i]);
}
for(int i=0;i<N-1;i++){
printf("%d:",v[i+1]);
auto it=st.find(Node(v[i],cnt[v[i]]));
if(it!=st.end()){
st.erase(it);
}
cnt[v[i]]++;
st.insert(Node(v[i],cnt[v[i]]));
int tcnt=0;
for(auto it=st.begin();tcnt<K&&it!=st.end();it++){
printf(" %d",it->id);
tcnt++;
}
printf("\n");
}
return 0;
}
水题,注意括号位置的取舍~
#include
using namespace std;
struct Node{
string val;
int left=-1;
int right=-1;
};
int N,root;
vector<Node>nodes;
vector<bool>isC;
string ans="";
bool flag=false;
void inTravel(int root){
if(root==-1){
return;
}else{
if((nodes[root].val=="-"||nodes[root].val=="+")&&nodes[root].right==-1){
if(nodes[root].left!=-1||nodes[root].right!=-1){
ans+="(";
flag=true;
}
ans+=nodes[root].val;
inTravel(nodes[root].left);
if(nodes[root].left!=-1||nodes[root].right!=-1){
ans+=")";
}
}else{
if(nodes[root].left!=-1||nodes[root].right!=-1){
ans+="(";
flag=true;
}
inTravel(nodes[root].left);
ans+=nodes[root].val;
inTravel(nodes[root].right);
if(nodes[root].left!=-1||nodes[root].right!=-1){
ans+=")";
}
}
return;
}
}
int main(){
scanf("%d",&N);
nodes.resize(N+1);
isC.resize(N+1);
fill(isC.begin(),isC.end(),false);
for(int i=1;i<=N;i++){
cin>>nodes[i].val>>nodes[i].left>>nodes[i].right;
if(nodes[i].left>0){
isC[nodes[i].left]=true;
}
if(nodes[i].right>0){
isC[nodes[i].right]=true;
}
}
for(int i=1;i<=N;i++){
if(!isC[i]){
root=i;
break;
}
}
inTravel(root);
int len=ans.length();
for(int i=0;i<len;i++){
if(flag&&(i==0||i==len-1))continue;
printf("%c",ans[i]);
}
return 0;
}
其实抛开转路线的问题,就是一个简单的寻找最短路径,用dfs即可,考虑转路线的问题,一开始我是用map存每一个结点在那几条路径上,发现问题被复杂化了,因为点虽然重合但是线是不重合的,因此在同等条件下,存储某条线属于哪一个路线是更好的选择。这里为了降低空间复杂度,将graph[u][v]=linex
该写成graph[u*10000+v]=linex
,用map存储,换路径的时候根据graph即可计算是否换乘了~
#include
using namespace std;
int N,M,K,st,ed,u,v,sign;
vector<vector<int>>graph(10000); //地铁路线
map<int,int>lines;
vector<int>vis(10000);
vector<int>path;//路线
const int inf=INT_MAX;
int min_sum,min_trans;
void dfs(int sum,int trans,int id,vector<int>temp,int line_id){
if(sum>min_sum||sum==min_sum&&trans>min_trans){
return;
}
sum++;
temp.push_back(id);
vis[id]=sign;
//printf("sign:%d id:%d\n",sign,id);
//判断终止的条件
if(id==ed){
if(sum<min_sum){
min_sum=sum;
min_trans=trans;
path=temp;
}else if(sum==min_sum&&trans<min_trans){
min_trans=trans;
path=temp;
}
}else{
//继续探路
for(int i=0;i<graph[id].size();i++){
int nid=graph[id][i];
if(vis[nid]==sign){
continue;
}else{
if(line_id!=-1&&lines[id*10000+nid]!=line_id){
dfs(sum,trans+1,nid,temp,lines[id*10000+nid]);
}else{
dfs(sum,trans,nid,temp,lines[id*10000+nid]);
}
}
}
}
//回溯
temp.pop_back();
vis[id]=sign-1;
}
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++){
scanf("%d",&M);
scanf("%d",&u);
for(int j=0;j<M-1;j++){
scanf("%d",&v);
//存储道路信息
graph[u].push_back(v);
graph[v].push_back(u);
lines[u*10000+v]=i;
lines[v*10000+u]=i;
u=v;
}
}
scanf("%d",&K);
for(int i=1;i<=K;i++){
vector<int>temp;
scanf("%d%d",&st,&ed);
sign=i;
min_sum=inf;
min_trans=inf;
dfs(0,0,st,temp,-1);
int len=path.size();
printf("%d\n",len-1);
int line_id=lines[path[0]*10000+path[1]];
for(int i=1;i<len;i++){
if(lines[path[i]*10000+path[i-1]]!=line_id){
printf("Take Line#%d from %04d to %04d.\n",line_id,st,path[i-1]);
st=path[i-1];
line_id=lines[path[i]*10000+path[i-1]];
}
}
printf("Take Line#%d from %04d to %04d.\n",line_id,st,path[len-1]);
}
return 0;
}
#include
using namespace std;
int M,N,S;
vector<string>ans;
string name;
map<string,bool>mp;
int main(){
scanf("%d%d%d",&M,&N,&S);
int flag=S;
for(int i=1;i<=M;i++){
cin>>name;
if(i==flag){
if(mp.find(name)==mp.end()){
flag+=N;
ans.push_back(name);
mp[name]=true;
}else{
flag+=1;
}
}
}
int len=ans.size();
if(len==0){
printf("Keep going...");
}else{
for(int i=0;i<len;i++){
printf("%s\n",ans[i].c_str());
}
}
return 0;
}
这题主要是找规律,基础的贪心
#include
using namespace std;
int N,temp,ans=0;
vector<int>v;
int main(){
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&temp);
v.push_back(temp);
}
sort(v.begin(),v.end());
ans=v[0];
for(int i=1;i<N;i++){
ans=(ans+v[i])/2;
}
printf("%d",ans);
return 0;
}
就算忘记欧拉图成立的条件,读懂题干的话也会写,而且会发现真的不难~
Eulerian
:
Semi-Eulerian
:
Non-Eulerian
:
#include
using namespace std;
//欧拉回路
int N,M,u,v;
vector<int>degree;
vector<vector<int>>graph;
vector<bool>vis;
void bfs(int id){
queue<int>q;
q.push(id);
while(!q.empty()){
int top=q.front();
vis[top]=true;
q.pop();
for(int i=0;i<graph[top].size();i++){
if(!vis[graph[top][i]]){
q.push(graph[top][i]);
}
}
}
return ;
}
int main(){
scanf("%d%d",&N,&M);
degree.resize(N+1,0);
graph.resize(N+1);
vis.resize(N+1,false);
for(int i=0;i<M;i++){
scanf("%d%d",&u,&v);
degree[u]++;
degree[v]++;
graph[u].push_back(v);
graph[v].push_back(u);
}
int odd=0,even=0,cnt=0;
for(int i=1;i<=N;i++){
if(!vis[i]){
bfs(i);
cnt++;
}
if(i!=1){
printf(" ");
}
printf("%d",degree[i]);
if(degree[i]%2==0){
even++;
}else{
odd++;
}
}
if(cnt==1&&even==N){
printf("\nEulerian");
}else if(odd==2){
printf("\nSemi-Eulerian");
}else{
printf("\nNon-Eulerian");
}
return 0;
}
每一层保存,按照奇偶决定当前层需不需要反转后再输出
#include
using namespace std;
int N;
vector<int>in,post;
vector<vector<int>>ans;
struct Node{
int val;
Node*left=NULL;
Node*right=NULL;
int level=0;
};
Node*build(int inL,int inR,int postL,int postR){
if(inL<0||inR>=N||postL<0||postR>=N||inR<inL||postR<postL){
return NULL;
}
Node*root=new Node;
root->val=post[postR];
int k;
for(int i=inL;i<=inR;i++){
if(in[i]==root->val){
k=i;
break;
}
}
int num=k-inL;
root->left=build(inL,k-1,postL,postL+num-1);
root->right=build(k+1,inR,postL+num,postR-1);
return root;
}
void levelTravel(Node*root){
queue<Node*>q;
q.push(root);
int level=0;
while(!q.empty()){
Node*top=q.front();
q.pop();
level=top->level;
ans[level].push_back(top->val);
if(top->left!=NULL){
top->left->level=level+1;
q.push(top->left);
}
if(top->right!=NULL){
top->right->level=level+1;
q.push(top->right);
}
}
return;
}
int main(){
scanf("%d",&N);
in.resize(N);
ans.resize(N);
post.resize(N);
for(int i=0;i<N;i++){
scanf("%d",&in[i]);
}
for(int i=0;i<N;i++){
scanf("%d",&post[i]);
}
Node*root=build(0,N-1,0,N-1);
levelTravel(root);
bool flag=false;
for(int i=0;i<ans.size();i++){
int len=ans[i].size();
if(len==0){
break;
}
if(i%2==0){
reverse(ans[i].begin(),ans[i].end());
}
for(int j=0;j<len;j++){
if(flag){
printf(" ");
}else{
flag=true;
}
printf("%d",ans[i][j]);
}
}
return 0;
}