1011——1020
1011[数学计算]
题目要求:数学计算而已
思路:整个数学公式
//
// @author prime on 2017/6/4.
//
#include <iostream>
using namespace std;
int main()
{
float res[3];
char choice[3];
float a,b,c;
for(int i=0;i<3;i++)
{
scanf("%f %f %f",&a,&b,&c);
if(a>=b&&a>=c)
{
res[i]=a;
choice[i]='W';
} else if(b>=a&&b>=c)
{
res[i]=b;
choice[i]='T';
} else if(c>=a&&c>=b)
{
res[i]=c;
choice[i]='L';
}
}
double profit=(res[0]*res[1]*res[2]*0.65-1)*2;
printf("%c %c %c %.2f",choice[0],choice[1],choice[2],profit);
return 0;
}
1012[排序]
题目要求:对成绩进行排序,输出每个人最好的排名。
思路:排序
这道题最好用一个数组保存每个人的最好成绩,这样空间换时间不容易出错。注意并列的情况,如果两个第一名成绩一样的话,就没有第二名了,直接下一个排第三。也就是说1 1 3 4 5而不是11 2 3 4这种布局。
排序用的标准库算法,方便的很,提前说一下,这个排序函数可以包含很多语意,后面的题目会遇到。
这里用循环的方法,每次针对一门课进行排序,记录相应的rank。注意最后的map查找操作,算法题最好用find
。
//
// @author prime on 2017/6/5.
//
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
class Student
{
public:
int id;
int score[4];//0下标代表平均分
int best;//最好的科目下标
int rank[4];
};
char course[4]={'A','C','M','E'};
int current;//不能声明成index,我就fuck了
bool cmp(Student s1,Student s2)
{//根据不同情况进行不同规则的排序。
return s1.score[current]>s2.score[current];
}
int main()
{
map<int,int> exist;
int n,m;
scanf("%d %d",&n,&m);
Student s[n];
for (int i=0;i<n;++i)
{
scanf("%d %d %d %d",&s[i].id,&s[i].score[1],&s[i].score[2],&s[i].score[3]);
s[i].score[0]=(s[i].score[1]+s[i].score[2]+s[i].score[3])/3.0+0.5;//平均数四舍五入
}
for(current=0;current<4;++current)
{//分别按照每门课进行排名
sort(s,s+n,cmp);
s[0].rank[current]=1;
for(int i=1;i<n;++i)
{
s[i].rank[current]=i+1;
if(s[i].score[current]==s[i-1].score[current])
s[i].rank[current]=s[i-1].rank[current];
}
}
for(int i=0;i<n;++i)
{
exist[s[i].id]=i;//记录存在的id和其下标的映射
int minx=s[i].rank[0];//记录最好科目的排名
s[i].best=0;//记录最好科目的下标
for(int j=1;j<4;j++)
{
if(s[i].rank[j]<minx)
{
minx=s[i].rank[j];
s[i].best=j;
}
}
}
for(int i=0;i<m;++i)
{
int id,k;
scanf("%d",&id);
if(exist.find(id)!=exist.end())//map如果没有对应的键值,就会插入键,然后值初始化。用at会抛出异常,算法题不适合。还是用这种方法吧。
{
k=exist[id];
printf("%d %c\n",s[k].rank[s[k].best],course[s[k].best]);
} else
{
printf("N/A\n");
}
}
return 0;
}
1013[连通分量+DFS]
题目要求:求最少添加几条边让这个图连通。
思路:求出图的连通分量个数即可,添加连通分量数-1条边。因为当a个互相分立的连通分量需要变为连通图的时候,只需要添加a-1个路线,就能让他们相连。
对于每一个被占领的城市,去除这个城市结点,就是把它标记为已经访问过,这样在深度优先遍历的时候,对于所有未访问的结点进行遍历,就能求到所有的连通分量的个数
//
// @author prime on 2017/6/6.
//
#include <iostream>
#include <algorithm>
using namespace std;
int G[1000][1000];
bool visited[1000];
int n;
void dfs(int source)
{
visited[source]= true;
for (int v=1;v<=n;++v)
{
if (!visited[v]&&G[source][v]==1)
{
dfs(v);
}
}
}
int main()
{
int m,k;
scanf("%d %d %d",&n,&m,&k);
fill(visited,visited+n+1, false);
for(int i=0;i<m;++i)
{
int u,v;
scanf("%d %d",&u,&v);
G[u][v]=G[v][u]=1;
}
for (int i=0;i<k;++i)
{
int v,res=0;
scanf("%d",&v);
visited[v]= true;
for (int j=1;j<=n;++j)
{
if(!visited[j])
{
dfs(j);
res++;//每当DFS一次,连通分量数加一。
}
}
fill(visited,visited+n+1, false);
printf("%d\n",res-1);
}
return 0;
}
1014[队列]
思路:对每个窗口设置一个结构体,两个成员记录第一个结束的时间和队列全部完成的时间,之所以记录第一个,是为了确定黄线外的人应该新加入哪条队列 ,而第二个是为了确定新加入的人是否sorry。注意,为了方便对时间统一处理,这里统一化成了分钟,这是很有用的思路。适当用goto语句也挺不错。
//
// @author prime on 2017/6/6.
//
#include <iostream>
#include <deque>
#include <vector>
using namespace std;
struct Window
{
deque<int> q;
int first_over;//队列第一个结束的时间
int last_over;//队列最后一个结束的时间
};
int main()
{
int N,M,K,Q;
scanf("%d %d %d %d",&N,&M,&K,&Q);
vector<int> time(K+1);
vector<bool> sorry(K+1, false);
int index=1;//处理到第几个顾客
vector<int> res(K+1);//记录每个顾客的结束时间
for(int i=1;i<=K;++i)
{
scanf("%d",&time[i]);
}
vector<Window> w(N+1);
for (int i=1;i<=M;++i)//队列中每一个
{
for (int j=1;j<=N;++j)//每个窗口
{
if(index<=K)
{
if(w[j].last_over>=540)//思维漏洞,没考虑黄线内的可能也会sorry
{
sorry[index]= true;
goto xixi;
}
w[j].q.push_back(time[index]);
w[j].last_over+=time[index];
if(i==1)
{//每队的第一个结束时间
w[j].first_over=time[index];
}
res[index]=w[j].last_over;//每次都是从最后开始排队,队伍结束就是自己结束
xixi:
index++;
}
}
}
while(index<=K)
{//有人在黄线外
int min_time=w[1].first_over;
int point=1;//记录下标
for (int i=2;i<=N;++i)
{
if(w[i].first_over<min_time)
{
min_time=w[i].first_over;
point=i;
}
}
if(w[point].last_over>=540)
{
sorry[index]= true;
goto hehe;
}
w[point].q.pop_front();
w[point].q.push_back(time[index]);
w[point].first_over+=w[point].q[0];
w[point].last_over+=time[index];
res[index]=w[point].last_over;
hehe:
++index;
}
for (int i=1,check;i<=Q;++i)
{
scanf("%d",&check);
if(sorry[check])
printf("Sorry\n");
else
{
int hour,minute;
minute=res[check]%60;
hour=res[check]/60;
printf("%02d:%02d\n",hour+8,minute);
}
}
return 0;
}
1015[素数+进制转换]
思路:很简单的思路,就是把各位反转后判断是否是素数即可。注意,如果原本就不是素数,直接输出否即可。
这里有两种思路,第一种是用对数函数判断位数,然后指数公式求解反转各位后的值。第二种是把各位放到数组中,然后累乘求出反转后的值。判断素数的函数也是比较经典的一种,背下来吧。
//
// @author prime on 2017/6/6.
//
#include <iostream>
#include <cmath>
using namespace std;
bool isprime(int num)
{
if(num<=1)//多加一个没坏处
return false;
for (int i=2;i<=sqrt(num);++i)
{
if(num%i==0)
return false;
}
return true;
}
int main()
{
int n,radix,reverse=0,digit;
while(scanf("%d",&n)!=EOF)
{
if(n<0)
break;
scanf("%d",&radix);
if(!isprime(n))
{
printf("No\n");
continue;
}
digit=log10(n)/log10(radix)+1;//这里不能用log10(n),因为基数是radix,用换底公式解决吧!
reverse=0;
for (int i=digit-1;i>=0;--i)
{
reverse+=(int)pow((double)radix,(double)i)*(n%radix);
n/=radix;
}
/*int len = 0;
int arr[100];
do{
arr[len++] = n % radix;
n = n / radix;
}while(n != 0);
for(int i = 0; i < len; i++) {
n = n * radix + arr[i];
}*/
if(isprime(reverse))
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
1016[排序]
思路:典型的排序问题。注意这里的排序函数的书写,后面很多题也用到这种形式。针对时间的计算依旧同一单位!
//
// @author prime on 2017/6/6.
//
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
struct Recode
{
string name;
int month,day,hour,minute,time;
string status;//on or off?
};
bool compare(Recode & s1,Recode &s2)
{//一条比较语句可以包含很多信息
return s1.name!=s2.name?s1.name<s2.name:s1.time<s2.time;
}
double calculate(const Recode &s,int const *cost, double a_day)
{
double total=0;
total+=cost[s.hour]*s.minute;
for (int i=0;i<s.hour;i++)
total+=60*cost[i];
return (total+s.day*a_day)/100.0;
}
int main()
{
int cost[24];
for (int i=0;i<24;++i)
{
scanf("%d",&cost[i]);
}
int N;
scanf("%d",&N);
vector<Recode> input(N);//必须先预先分配足够的空间
for(int i=0;i<N;++i)
{
cin>>input[i].name;
scanf("%d:%d:%d:%d",&input[i].month,&input[i].day,&input[i].hour,&input[i].minute);
input[i].time=input[i].day*24*60+input[i].hour*60+input[i].minute;
cin>>input[i].status;
}
sort(input.begin(),input.end(),compare);
map<string,vector<Recode>> res;
vector<Recode>::iterator it=input.begin()+1,pre_it=input.begin();
while(it!=input.end())
{
if(pre_it->name==it->name&&pre_it->status=="on-line"&&it->status=="off-line")
{
res[it->name].push_back(*pre_it);
res[it->name].push_back(*it);
}
it++;
pre_it++;
}
double a_day=0;//一天的消费
for (int i=0;i<24;i++)
{
a_day+=cost[i]*60;
}
for (auto& e:res)
{
vector<Recode> tmp=e.second;
cout<<e.first;
printf(" %02d\n",tmp[0].month);
double total=0;
for (int i=0;i<tmp.size();i+=2)
{
printf("%02d:%02d:%02d %02d:%02d:%02d ",tmp[i].day,tmp[i].hour,tmp[i].minute,tmp[i+1].day,tmp[i+1].hour,tmp[i+1].minute);
printf("%d ",tmp[i+1].time-tmp[i].time);
double co=calculate(tmp[i+1],cost,a_day)-calculate(tmp[i],cost,a_day);
printf("$%.2f\n",co);
total+=co;
}
printf("Total amount: $%.2f\n",total);
}
return 0;
}
1017[排序+队列]
思路:
本题是对现实的模拟。首先,先对所有人按到达时间进行排序(和往常一样,时间全部弄成秒)。对每个窗口,要记录其结束时间,这是为了确定黄线外队伍第一个人到哪个窗口进行排队。每当进入排队时,窗口结束时间就增加;如果来的时间晚于窗口结束时间,说明不需要等待,直接服务,窗口结束时间就是来的时间+服务时间。题目要求的是等待时间,不算服务时间的!!!
//
// @author prime on 2017/6/6.
//
#include <iostream>
#include <algorithm>
using namespace std;
struct Customer
{
int arrive;//到达时间
int take;//服务所需时间
};
bool cmp(const Customer& s1,const Customer &s2)
{
return s1.arrive<s2.arrive;
}
int main()
{
int N,K;//N个顾客,K个窗口
scanf("%d %d",&N,&K);
vector<Customer> c;
for (int i=0;i<N;++i)
{
int hh,mm,ss,tt;
scanf("%d:%d:%d %d",&hh,&mm,&ss,&tt);
int s=hh*3600+mm*60+ss;
if(s>61200)//5点之后来的不被服务
continue;
Customer tmp;
tmp.arrive=s;
tmp.take=tt*60;
c.push_back(tmp);
}
if(c.size()==0)
{
printf("0.0");
return 0;
}
int window[K]={28800};//每个窗口的结束时间 fuck,这个只初始化第一个元素。!!!!!!
fill(window,window+K,28800);
sort(c.begin(),c.end(),cmp);
double total_time=0.0;
for(int index=0;index<c.size();++index)
{
int fast=window[0],point=0;
for (int i=1;i<K;++i)
{
if(window[i]<fast)
{
fast=window[i];
point=i;
}
}
//point指向最快的那个窗口
if(window[point]<=c[index].arrive)//窗口结束时间早于顾客来,不用等待
{
window[point]=c[index].arrive+c[index].take;
} else
{
total_time+=(window[point]-c[index].arrive);//需要等待
window[point]+=c[index].take;//窗口结束时间增加
}
}
printf("%.1f",total_time/60.0/c.size());
return 0;
}
1018[Dijkstra+DFS]
思路:
首先进行Dijkstra算法求最短路径,在进行的过程中记录每个结点最近的前驱结点,距离一样就都记录下来。然后进行DFS,从终点开始,边递归边记录路径,到达起点之时,从起点开始沿着路径计算需要带过去多少自行车,带回来多少,然后进行全局变量值更新即可。
//
// @author prime on 2017/6/7.
//
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int inf=99999999;
int G[510][510];
int dist[510];
bool visit[510];
vector<int> pre[510];
int current_capacity[510];
int min_take=inf,min_back=inf;
vector<int> path,tmp_path;
int C,N,Sp,M;
void DFS(int v);
int main()
{
scanf("%d %d %d %d",&C,&N,&Sp,&M);
fill(G[0],G[0]+(500+10)*(500+10),inf);
fill(dist,dist+510,inf);
fill(visit,visit+510, false);
dist[0]=0;
for (int i=1;i<=N;++i)
{
scanf("%d",¤t_capacity[i]);
}
for (int i=0;i<M;++i)
{
int u,v,cost;
scanf("%d %d %d",&u,&v,&cost);
G[u][v]=cost;
G[v][u]=cost;
}
/*dijkstra*/
for(int i=0;i<=N;++i)
{
int min_dist=inf,u=-1;
for (int j=0;j<=N;++j)
{
if(!visit[j]&&dist[j]<min_dist)
{
u=j;
min_dist=dist[j];
}
}
if(u==-1)
break;
visit[u]=true;
for (int v=0;v<=N;++v)
{
if(!visit[v]&&G[u][v]!=inf)
{
if(dist[u]+G[u][v]<dist[v])
{
dist[v]=dist[u]+G[u][v];
pre[v].clear();
pre[v].push_back(u);//记录前一个结点
} else if(dist[u]+G[u][v]==dist[v])
{
pre[v].push_back(u);
}
}
}
}
DFS(Sp);//从终点倒着DFS
printf("%d 0",min_take);
for (int i=path.size()-2;i>=0;i--)
{
printf("->%d",path[i]);
}
printf(" %d",min_back);
return 0;
}
void DFS(int v)
{//tmp_path[0]是终点,DFS的过程是从终点到起点。。
tmp_path.push_back(v);
if(v==0)
{//从起点到终点进行统计
int take=0,back=0;//带来几辆车,带回几辆车?
for (int i=tmp_path.size()-2;i>=0;--i)
{//从离起点最近的点开始
int id=tmp_path[i];
if(current_capacity[id]>C/2)
{
back+=(current_capacity[id]-C/2);
}
else if(current_capacity[id]<C/2)
{
int odd=C/2-current_capacity[id];//缺几辆车?
if(back<odd)
{
take+=odd-back;
back=0;
} else
{
back-=odd;
}
}
}
if(take<min_take)
{
min_take=take;
min_back=back;
path=tmp_path;
} else if(min_take==take&&back<min_back)
{
min_back=back;
path=tmp_path;
}
}
else
{
for (int i=0;i<pre[v].size();++i)
{
DFS(pre[v][i]);//v的前驱结点全部递归
}
}
tmp_path.pop_back();
}
1019[进制转换+回文数]
思路:
直接进制转换,然后对数组遍历即可。这里实现还是比较繁琐,最好用string,可以reverse之后看是否相等,就避免自己写判断,少写一点bug就越少啊。。
//
// @author prime on 2017/6/7.
//
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int N,b;
scanf("%d %d",&N,&b);
vector<int> res;
if(N==0)
{
printf("Yes\n0");
return 0;
}
while(N!=0)
{
res.push_back(N%b);
N/=b;
}
vector<int>::iterator it1=res.begin(),it2=res.end()-1;
int flag=true;
while(it1<it2)
{
if(*(it1++)!=*(it2--))
{
flag=false;
break;
}
}
if(flag)
{
printf("Yes\n");
} else
{
printf("No\n");
}
for (int i=res.size()-1;i>0;i--)
{
printf("%d ",res[i]);
}
printf("%d",res[0]);
return 0;
}
1020[已知两个遍历序列求另一个]
思路:
给了后序和中序,可以根据它们重建二叉树,在数据结构复习一文中已经总结过。但是,当我傻傻的重新建立二叉树再遍历之后,才知道,原来可以不用建立树直接得出结果!too young too simple
笨办法:
//
// @author prime on 2017/6/7.
//
#include <iostream>
#include <deque>
/*特殊用例 111*/
using namespace std;
struct Node
{
int value;
Node *left= nullptr;
Node *right= nullptr;
Node(int v)
{
value=v;
}
};
Node *Build_tree(int *in_order,int *post_order,int length);
int main()
{
int N;
scanf("%d",&N);
int post_order[N],in_order[N];
for (int i=0;i<N;++i)
{
scanf("%d",&post_order[i]);
}
for (int i=0;i<N;++i)
scanf("%d",&in_order[i]);
Node *root=Build_tree(in_order,post_order,N);
/*if(root)
printf("%d",root->value);*/
deque<Node> queue;
Node *p=root;
queue.push_back(*p);
while(true)
{
*p=queue[0];
if(p->left)
queue.push_back(*p->left);
if(p->right)
queue.push_back(*p->right);
if(queue.size()==1)
{
printf("%d",queue[0].value);
return 0;
}
queue.pop_front();
printf("%d ",p->value);
}
}
Node *Build_tree(int in_order[],int post_order[],int length)//length代表中序序列长度
{
if(length<=0)
{
return nullptr;
}
Node *root=new Node(post_order[length-1]);
for(int i=0;i<length;++i)
{
if(in_order[i]==post_order[length-1])
{
int left_length=i,right_length=length-i-1;
root->left=Build_tree(in_order,post_order,left_length);
root->right=Build_tree(in_order+left_length+1,post_order+left_length,right_length);
break;
}
}
return root;
}
机智法:
首先先看已知后序和中序求先序:就和已经总结的二叉树知识一样,就是分割序列!
//
// @author prime on 2017/6/15.
//
#include <iostream>
using namespace std;
int post[] = {3, 4, 2, 6, 5, 1};
int in[] = {3, 2, 4, 1, 6, 5};
int pre[]={1,2,3,4,5,6};
void preorder(int root,int start,int end)
{//root是后序遍历的下标,代表根节点。start和end代表中序序列的边界。
if(start>end)
return;
int i=start;
while(i<end&&post[root]!=in[i])
i++;//i是中序序列中根的下标
cout<<in[i]<<" ";
preorder(root-(end-i+1),start,i-1);
preorder(root-1,i+1,end);
}
void postorder(int root,int start,int end)
{
if(start>end)
return;
int i=start;
while(i<end&&in[i]!=pre[root]) ++i;
postorder(root+1,start,i-1);
postorder(root+(i-start)+1,i+1,end);
cout<<in[i]<<" ";
}
int main()
{
//preorder(5,0,5);
postorder(0,0,5);
return 0;
}
下面就是本题了,只需要简单添加一个数组level和一个变量index,数组一开始初始化-1,然后在上述求中序序列的过程中,index表示当前根节点的下标(从1开始,在level中记录),最后从头到尾遍历level数组,把非-1的值输出即可!——其实就是用线性结构存二叉树。2*index
表示左孩子 ,2*index+1
表示右孩子,同样,这里也有线性结构保存二叉树的通病,level必须很大!!!
//
// @author prime on 2017/6/15.
//
#include <iostream>
#include <algorithm>
using namespace std;
int post[30];
int in[30];
int level[10000];
void pre_order(int root,int start,int end,int index)
{
if(start>end)
return;
int i=start;
while(i<end&&post[root]!=in[i]) i++;
level[index]=post[root];
pre_order(root-(end-i+1),start,i-1,2*index);
pre_order(root-1,i+1,end,2*index+1);
}
int main()
{
fill(level,level+10000,-1);
int N;
scanf("%d",&N);
for (int i=0;i<N;++i)
scanf("%d",&post[i]);
for (int i=0;i<N;++i)
scanf("%d",&in[i]);
pre_order(N-1,0,N-1,1);
printf("%d",level[1]);
int count=1;
for (int i=2;i<10000;i++)
{
if(count==N)
break;
if(level[i]!=-1)
{
printf(" %d",level[i]);
count++;
}
}
return 0;
}
算是一种奇淫技巧吧~