总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716
信息学奥赛一本通(C++版) 第三部分 数据结构 第三章 树
http://ybt.ssoier.cn:8088
第一节 二叉树
//1336 【例3-1】找树根和孩子
//提交,未通过,明白了,孩子必须按字典序输出
//修改,提交,AC 2017-12-13 18:54
//该题思路可以预计,与书中提供的代码很不相同,书中猜测用的是左子右兄表示法,日后验证
//该题,本人思路,邻接表,有向图.
//很明显,水平上了一个台阶。
#include <stdio.h>
#include <string.h>
int parent[110],head[110],cnt=0,d[110];//parent[i]节点i的父亲,d[i]存储max的孩子
struct node{
int to,next;
}e[210];//有向图
void add_edge(int u,int v){
cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int main(){
int x,y,n,m,i,max=0,b,count,j,t;
memset(parent,0,sizeof(parent));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
scanf("%d%d",&x,&y);
parent[y]=x,add_edge(x,y);
}
for(i=1;i<=n;i++)//找root
if(parent[i]==0)printf("%d\n",i);
for(i=1;i<=n;i++){//找孩子最多的节点
count=0;
for(b=head[i];b;b=e[b].next)
count++;
if(count>max)max=count,j=i;
}
count=0;
for(b=head[j];b;b=e[b].next)d[count++]=e[b].to;//存储孩子
printf("%d\n",j);
for(i=0;i<count;i++)//自小到大排序
for(j=i+1;j<count;j++)
if(d[i]>d[j])t=d[i],d[i]=d[j],d[j]=t;
for(i=0;i<count;i++)printf("%d ",d[i]);
return 0;
}
//1337 【例3-2】单词查找树
//首先明确,是一棵树,而不是二叉树
//第一直觉是采用树的处理方式,至少是递归
//无奈,眼高手低,
//搜索网络,https://www.cnblogs.com/sssy/p/6640480.html此文代码写得不错,虽然没有什么说明,但还是看懂代码了
//经检验,上述代码AC,可以开始独立编写了
//样例通过,提交 测试点4 答案错误
//静态阅读代码,发现 漏了 读入的字符串 排序
//添加,提交,AC 2017-12-19 18:40
#include <stdio.h>
#include <string.h>
char a[32000][70];
void quicksort(int left,int right){//字符串处理的快排,自小到大排序
int i=left,j=right,mid=(left+right)/2;
char b[70];
while(i<=j){
while(strcmp(a[i],a[mid])<0)i++;
while(strcmp(a[mid],a[j])<0)j--;
if(i<=j){
strcpy(b,a[i]);
strcpy(a[i],a[j]);
strcpy(a[j],b);
i++,j--;
}
}
if(i<right)quicksort(i,right);
if(left<j)quicksort(left,j);
}
int main(){
int ans,i=0,j,len;
while(scanf("%s",a[i])!=EOF)i++;//字符串序列读取,序列从0开始
len=i;
quicksort(0,len-1);//遗漏此句 ,测试点4 答案错误
ans=strlen(a[0]);
for(i=1;i<len;i++){
j=0;
while(a[i-1][j]==a[i][j])j++;//查寻雷同部分
ans+=strlen(a[i])-j;//不同部分,就需开新节点来存储
}
ans+=1;//加上root元素
printf("%d",ans);
return 0;
}
//1338 【例3-3】医院设置
//https://www.cnblogs.com/harden/p/5596801.html看了文中才明白,要用Floyd算法,想象与现实差距太大
//样例通过,提交AC 2017-12-20
#include <stdio.h>
#include <string.h>
#define INF 999999999
int a[110][110],b[110],d[110];
int main(){
int i,j,n,k,left,right,min=INF;//此处写成 min=0 昏招
memset(d,0,sizeof(d));
scanf("%d",&n);
for(i=1;i<=n;i++)//矩阵初始化
for(j=1;j<=n;j++)
if(i==j)a[i][j]=0;
else a[i][j]=INF;
for(i=1;i<=n;i++){
scanf("%d%d%d",&b[i],&left,&right);
if(left!=0)a[i][left]=a[left][i]=1;//无向图
if(right!=0)a[i][right]=a[right][i]=1;
}
for(k=1;k<=n;k++)//Floyd算法
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(a[i][j]>a[i][k]+a[k][j])
a[i][j]=a[i][k]+a[k][j];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
d[i]+=a[i][j]*b[j];
for(i=1;i<=n;i++)
if(d[i]<min)
min=d[i];
printf("%d",min);
return 0;
}
//1339 【例3-4】求后序遍历
//样例通过,提交,测试点3-5 答案错误
//急寻测试数据
//提供一组测试数据
//输入数据
//abdcef
//bdaecf
//输出数据
//dbefca
//核心,先序字符串对应先序字符串,中序字符串对应中序字符串
//笔误造成排查很久,for(i=len2+1;i<len1;i++)e[i-(len2+1)]=a[i];//此处写成 e[i-(len2+1)]=b[i] 昏招,修改,提交AC 2017-12-14
#include <stdio.h>
#include <string.h>
char pre[100],in[100];
void PostOrder(char *a,char *b){//a先序 b中序
char c[100],d[100],e[100],f[100];//c先序 d中序 e先序 f中序
int len1,len2,i,j,k;
len1=strlen(a);
if(len1==0)return;
for(k=0;k<len1;k++)
if(b[k]==a[0])break;
for(i=0;i<k;i++)d[i]=b[i];
d[i]='\0',len2=strlen(d);
for(i=1;i<len2+1;i++)c[i-1]=a[i];
c[i-1]='\0';
for(i=k+1;i<len1;i++)f[i-(k+1)]=b[i];
f[i-(k+1)]='\0';
for(i=len2+1;i<len1;i++)e[i-(len2+1)]=a[i];//此处写成 e[i-(len2+1)]=b[i] 昏招
e[i-(len2+1)]='\0';
PostOrder(c,d);
PostOrder(e,f);
printf("%c",a[0]);
}
int main(){
scanf("%s%s",pre,in);
PostOrder(pre,in);
return 0;
}
//1340 【例3-5】扩展二叉树
//http://www.oier.cc/ssoj2464%e6%89%a9%e5%b1%95%e4%ba%8c%e5%8f%89%e6%a0%91/此文代码写得很合心意
//样例通过,提交AC 2017-12-20
#include <stdio.h>
char d[1000];
void pre_order(int k){
scanf("%c",&d[k]);
if(d[k]=='.')return;
pre_order(2*k);//左子树
pre_order(2*k+1);//右子树
}
void in_order(int k){
if(d[k]=='.')return;
in_order(2*k);
printf("%c",d[k]);
in_order(2*k+1);
}
void post_order(int k){
if(d[k]=='.')return ;
post_order(2*k);
post_order(2*k+1);
printf("%c",d[k]);
}
int main(){
int i;
pre_order(1);
in_order(1);
printf("\n");
post_order(1);
return 0;
}
//1363 小球(drop)
//树的特征,基本上是要上递归
//网络上搜了一通,全是用非递归地做法,基本都是《算法竞赛入门经典(第2版)》的产物.
//翻看《信息学奥赛一本通(C++)》受到启发,进行修改,样例通过,提交AC 2017-12-15
//还是那句,树要与递归结合,这是树特有的特点。
#include <stdio.h>
#include <string.h>
int D,I,b[1<<20],ans;
void down(int k){
if((1<<(D-1))<=k&&k<(1<<D)){//到达叶节点
ans=k;//ans全局变量,保存球落到的最新叶节点
return;//结束条件
}
if(b[k])b[k]=!b[k],down(2*k+1);
else b[k]=!b[k],down(2*k);
}
int main(){
int i,a;
memset(b,0,sizeof(b));
scanf("%d%d",&D,&I);
for(i=1;i<=I;i++)down(1);//此处写成 down(i),这个确实很难,需要调试才能找出错误
printf("%d",ans);
return 0;
}
//1364 二叉树遍历(flist)
//http://blog.csdn.net/yanyanwenmeng/article/details/77833274比较欣赏本文代码写法
//解题思路摘抄如下:这种题一般都要先确定好根节点的位置。然后才根据先序、中序、后序的规则进行递归。
//样例通过,提交AC 2017-12-20 18:32
#include <stdio.h>
#include <string.h>
int len;
char a[1000],b[1000];//a中序遍历 b层次遍历
void pre_order(int left,int right){
int i,j,flag=0;
for(i=0;i<len;i++){//对应层次遍历
for(j=left;j<=right;j++)//对应中序遍历
if(b[i]==a[j]){//找根
printf("%c",a[j]);
flag=1;
break;
}
if(flag==1)break;
}
if(left<j)pre_order(left,j-1);//左子树
if(j<right)pre_order(j+1,right); //右子树
}
int main(){
scanf("%s%s",a,b);
len=strlen(a);
pre_order(0,len-1);
return 0;
}
//1365 FBI树(fbi)
1.洛谷 P1087 FBI树
NOIP 2004 普及组 复赛 FBI树
1.阅读题目,还有些不知所云。
2.对样例进行手动模拟,弄明白题意了。
10001011
FBI树如下图所示
F
F F
F B F I
I B B B I B I I
1) T的根结点为R,其类型与串S的类型相同;此句是核心中的核心,也即F B I三种根节点。
3.接下来编程实现是重点。先建树,子弟相信行一层一层建树,用一维数组存储,第1号(第0号不使用)元素开始使用数组。父k,左子2*k,右子2*k+1。再进行遍历,后序遍历,采用递归的方式。
4.建树函数,遍历函数编写。
5.代码编写完成,提交AC,递归函数写法,记得先写终结条件。
6.此文写得不错,读者也可以借鉴。http://blog.csdn.NET/dogeding/article/details/52727239
7.此题,可以借鉴的是:二叉树的存储,二叉树的遍历。
附上AC代码,编译环境Dev-C++4.9.9.2
#include <stdio.h>
int n;
char a[1024+10];
char b[2048+10];
void build(char *s,int n){//建树代码
//底层构造
int i,j=0;
for(i=(1<<n);i<=(1<<(n+1))-1;i++){
b[i]=a[j++]=='0'?'B':'I';
}
//自底向上构造
for(i=n-1;i>=0;i--)
for(j=(1<<i);j<=(1<<(i+1))-1;j++)
if(b[2*j]=='B'&&b[2*j+1]=='B')
b[j]='B';
else if(b[2*j]=='I'&&b[2*j+1]=='I')
b[j]='I';
else
b[j]='F';
}
void visit(int node){//后序遍历代码
if(node>(1<<(n+1))-1)
return;
visit(node*2);//左子树
visit(node*2+1);//右子树
printf("%c",b[node]);//根
}
int main(){
scanf("%d",&n);
scanf("%s",a);
build(a,n);
visit(1);
printf("\n");
return 0;
}
//1366 二叉树输出(btout)
//样例解释如下图所示
//输出结果,按先序遍历,难点,在于计算各个节点的长度
//相当惊讶,程序竟然一次性编写,样例通过,
//提交,测试点2,4,5运行错误,明白,不是数组开得太小,就是递归出了问题
//将数组a[1000]开到a[100000]还是 上述错误,明白,此题必须改成循环的形式
//程序需要大改,
//http://www.oier.cc/ssoj2467%e4%ba%8c%e5%8f%89%e6%a0%91%e8%be%93%e5%87%ba/参考该文写法,感觉与本人还是很接近
//对照代码,发现,漏了 if(left_b<i) if(i<right_b)两句
//提交AC ,弄了半天,自己的代码写得还可以,错的是没考虑 判断条件了
//还是那句,树一定要找递归 2018-1-1 20:41
#include <stdio.h>
#include <string.h>
char a[100000],b[100000],c[100000];//a先序遍历 b中序遍历
int count(int left_a,int right_a,int left_b,int right_b){
int i,j;
if(left_a==right_a){
c[left_a]=1;
return c[left_a];
}
for(i=left_b;i<=right_b;i++)
if(a[left_a]==b[i])break;
if(left_b<i)c[left_a]+=count(left_a+1,(i-1-left_b)+(left_a+1),left_b,i-1);//漏了if(left_b<i)//左子树
if(i<right_b)c[left_a]+=count(right_a-(right_b-(i+1)),right_a,i+1,right_b);//漏了if(i<right_b)//右子树
return c[left_a];
}
int main(){
int lena,lenb,i,j;
scanf("%s%s",a,b);
lena=strlen(a),lenb=strlen(b);
count(0,lena-1,0,lenb-1);
for(i=0;i<lena;i++){
for(j=1;j<=c[i];j++)
printf("%c",a[i]);
printf("\n");
}
return 0;
}
//1367 查找二叉树(tree_a)
//看完题目,读完题解,才明白,一个数即查找的结点编号 指的是 中序遍历访问的第几个节点
//http://www.oier.cc/ssoj2469%e6%9f%a5%e6%89%be%e4%ba%8c%e5%8f%89%e6%a0%91/此文代码写得不错
//同时也参考了,《信息学奥赛一本通(C++版)》
//样例通过,提交AC 2017-12-21
#include <stdio.h>
#include <stdlib.h>
struct node{
int d,left,right;
}q[110];
int n,ans=0,a;
void in_order(int k){
if(q[k].left)in_order(q[k].left);
ans++;
if(q[k].d==a){
printf("%d",ans);
exit(0);//退出程序
}
if(q[k].right)in_order(q[k].right);
}
int main(){
int i;
scanf("%d%d",&n,&a);
for(i=1;i<=n;i++)scanf("%d%d%d",&q[i].d,&q[i].left,&q[i].right);//按层次遍历
in_order(1);
return 0;
}
//1368 对称二叉树(tree_c)
//http://blog.csdn.net/u011123263/article/details/20221171此文代码可以参考
//同时也参考了《信息学奥赛(C++版)》的写法
//样例通过,提交,测试点3 答案错误
//修改,提交,AC 2017-12-21
#include <stdio.h>
#include <string.h>
char s[1000];
int main(){
int i,len,b=1;
scanf("%s",s);
len=strlen(s);
s[len]='#';//添加此句,测试点3 答案正确,明白是数据越界 遇到错误
for(i=1;i<len;i+=2)//根节点s[0],跳过
if((s[i]=='#'&&s[i+1]!='#')||(s[i+1]=='#'&&s[i]!='#')){//不对称,比对称好写
b=0;
break;
}
if(b)printf("Yes");
else printf("No");
return 0;
}
2018-1-1 20:41 AC 该节内容
第二节 堆及其应用
//1369 合并果子(fruit)
//早就想用堆来编写该题,火候到了,可以动手了。
//因新堆要放入剩下的堆中进行比较,处理,故堆排序处理该题十分理想
//手动模拟了样例2的过程
//(1 1) 2 3 4 4 5 5 6 7 ans=2
//(2 2) 3 4 4 5 5 6 7 ans=2+4
// (3 4) 4 4 5 5 6 7 ans=2+4+7
// (4 4) 5 5 6 7 7 ans=2+4+7+8
// (5 5) 6 7 7 8 ans=2+4+7+8+10
//(6 7) 7 8 10 ans=2+4+7+8+10+13
//(7 8) 10 13 ans=2+4+7+8+10+13+15
//(10 13) 15 ans=2+4+7+8+10+13+15+23
//(15 23) ans=2+4+7+8+10+13+15+23+38=120
//手动模拟成功,那么编程成功也不远了
//样例通过,提交AC 该题测试数据明显没有考虑n==1的情况
//为了保证程序的健壮性,本人再加上n==1情况
//AC 2017-12-22 21:49 堆掌握得还可以了。
#include <stdio.h>
int heap[30100],s=0;
void add_heap(int k){//最小堆
int p;
s++,p=s;
while(p>1&&heap[p/2]>k){
heap[p]=heap[p/2];
p/=2;
}
heap[p]=k;
}
void adjust_heap(int t){
int left=2*t,right=2*t+1,k=t,a;
if(left<=s)k=heap[left]<heap[k]?left:k;
if(right<=s)k=heap[right]<heap[k]?right:k;
if(k!=t){
a=heap[k],heap[k]=heap[t],heap[t]=a;
adjust_heap(k);
}
}
void del_heap(){
heap[1]=heap[s];
s--;
adjust_heap(1);
}
int main(){
int n,ans=0,i,a,b;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a),add_heap(a);
if(n==1){
printf("%d",a);
return 0;
}
for(i=1;i<=n-1;i++){
a=heap[1],del_heap();
b=heap[1],del_heap();
ans+=a+b,add_heap(a+b);
}
printf("%d",ans);
return 0;
}
//1369 合并果子(fruit)
1.洛谷 p1090 合并果子
NOIP 2004 提高组 复赛 fruit 合并果子
1.读完题,马上意识到,该问题就是一棵哈夫曼树(赫夫曼树)。
2.刚好学过这个算法,但还没有编过码,感觉是手到擒来。
3.看了n的范围,冒泡排序会超时,采用快速排序。采用自大到小方式,逐步缩小数据长度。
4.提交40分,测试点5-10全部超时。
5.要改进算法。想想也是算法复杂度O(n*n*logn)不超时才怪。
6.上网搜索,发现堆的概念,值得一学。
7.堆排序需要消化,那先采用快速排序与插入排序。
附上采用快速排序与插入排序,提交AC的代码,编译环境Dev-C++4.9.9.2
#include <stdio.h>
int a[10000+100];
void quicksort(int left,int right){
int i=left,j=right,t;
int mid=a[(i+j)/2];
while(i<=j){
while(a[i]>mid)
i++;
while(a[j]<mid)
j--;//此处写成j++笔误
if(i<=j){
t=a[i];
a[i]=a[j];
a[j]=t;
i++;
j--;
}
}
if(left<j)//出处漏写
quicksort(left,j);
if(i<right)//此处漏写
quicksort(i,right);
}
int main(){
int n,i,j,ans=0,t;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
quicksort(1,n);
while(1){
t=a[n-1]+a[n];
ans=ans+t;
if(n==2)
break;
if(a[n-2]>=t)//插入排序
a[n-1]=t;
else{
j=n-2;
while(j>=1&&a[j]<t)//此处漏写j>=1查了会,只对了测试点1
j--;
for(i=n-1;i>j+1;i--)
a[i]=a[i-1];
a[i]=t;
}
n--;
}
printf("%d\n",ans);
return 0;
}
//1370 最小函数值(minval)
4.//洛谷 P2085 最小函数值
//题意不明,样例的输入,如何得出输出的数据,
//该题若用堆,用最小堆即可解决。关键是题意不明。
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P2085 摘抄如下
/*
每个函数的a,b,c都是正数
所以每个抛物线的对称轴都是负数
所以每条抛物线在x>=1时都是增函数
所以fi(1)一定是该抛物线最小值
所以首先把所有的fi(1)都放到优先队列里面
每次直接输出优先队列的top值
然后把top值所对应的函数的x+1的值放到队列里面
循环n次就是答案
*/
//虽然上面用的是优先队列,但思路是一样的。
//困扰在于如何插入m个有效的数,而且算法复杂度不高。
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P2085 摘抄如下
//算法:用一个数组记录当前第i个函数自变量的值,初始化X[i]=1,每次从小根堆取出一个函数值时,
//使其对应的自变量的值增加,比如取出的是第i个,就X[i]++,这样就是一个O(n+m)的算法
//请注意:(x∈N*)表示,x=1,2,3,...
//样例很快通过,可惜洛谷 502 Bad Gateway 2017-7-4
//提交AC
#include <stdio.h>
#define maxn 10000+100
int s=0;
struct node1{
int a,b,c;
}d[maxn];//?
struct node2{
int v,x,a;//a表示序列
}heap[maxn],tmp;
int f(int x,int a,int b,int c){
return a*x*x+b*x+c;
}
void insert(struct node2 k){//最小堆
int p;
s++;
p=s;
while(p>1&&k.v<heap[p/2].v){
heap[p]=heap[p/2];
p/=2;
}
heap[p]=k;
}
void adjust(int t){//位置
int left=2*t,right=2*t+1,k=t;
if(left<=s)k=heap[k].v<heap[left].v?k:left;
if(right<=s)k=heap[k].v<heap[right].v?k:right;
if(k!=t){
tmp=heap[k];
heap[k]=heap[t];
heap[t]=tmp;
adjust(k);
}
}
int main(){
int n,m,a,b,c,i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%d%d%d",&a,&b,&c);
d[i].a=a,d[i].b=b,d[i].c=c;
tmp.v=f(1,a,b,c),tmp.a=i,tmp.x=1;
insert(tmp);
}
for(i=1;i<=m;i++){
printf("%d ",heap[1].v);
heap[1].x++,j=heap[1].a,heap[1].v=f(heap[1].x,d[j].a,d[j].b,d[j].c);
adjust(1);
}
return 0;
}
//1371 看病
//样例通过,提交AC 2017-12-13
#include <stdio.h>
#include <string.h>
struct node{
char name[30];
int v;
}heap[100100],h_t;//最大堆
int s=0;//堆的长度
void add_heap(struct node hp){
int p;
s++,p=s;
while(p>1&&heap[p/2].v<hp.v){
heap[p]=heap[p/2],p/=2;
}
heap[p]=hp;
}
void adjust_heap(int t){
int left=2*t,right=2*t+1,k=t;
//找出父,左子,右子,最大值位置
if(left<=s)k=heap[k].v>heap[left].v?k:left;
if(right<=s)k=heap[k].v>heap[right].v?k:right;
if(t!=k){
h_t=heap[k],heap[k]=heap[t],heap[t]=h_t;
adjust_heap(k);
}
}
void del_heap(){
heap[1]=heap[s],s--;
adjust_heap(1);
}
int main(){
int t;
char cmd[10];
struct node hp;
scanf("%d",&t);
while(t--){
scanf("%s",cmd);
if(strcmp(cmd,"pop")==0){
if(s==0)printf("none\n");
else{
printf("%s %d\n",heap[1].name,heap[1].v);
del_heap();
}
}else if(strcmp(cmd,"push")==0){
scanf("%s%d",&hp.name,&hp.v);
add_heap(hp);
}
}
return 0;
}
//1372 小明的账单
//最大堆,最小堆
//样例通过,提交,只有 测试点1,2,3答案正确
//最小堆,最大堆,用完后,在最大堆里删除最小堆里的第一个元素,在最小堆里删除最大堆里的第一个元素
//翻看http://blog.csdn.net/thinfatty/article/details/54234004代码,觉得删除部分写得太复杂,
//还是自己动手,丰衣足食
//添上删除元素功能,提交 ,只有 测试点1,2,3答案正确
//添加边界判定, 提交 ,只有 测试点1,2,3答案正确
//仔细看了提交记录,发现错误,全是运行错误,明白是数组开得太小
//重新读题,发现理解错误,数组是开得太小了 int min_s=0,max_s=0,min_h[1500100],max_h[1500100];//此处写成 min_h[15100],max_h[15100];
//修改,提交,测试点7-10运行超时。
//将 “最小堆,最大堆,用完后,在最大堆里删除最小堆里的第一个元素,在最小堆里删除最大堆里的第一个元素” 功能删除,提交AC
//该题只考,最大堆,最小堆,不要想太复杂,2017-12-14
//经确认,以下代码只有80分,错了测试点4,5,上述AC是因评测系统错误造成的。2017-12-18 21:06
//https://www.cnblogs.com/Zhoier-Zxy/p/8075523.html 参看此文代码,思路摘抄如下:
//这里我用的是multi set啊!主要是利用它能erase一个数,这个还是很方便的;这里要注意r=*q.begin(),其实会想问:哎呀,中间的“ * ”啥意思啊。因为这里的begin或者end操作是取地址的(包括迭代器),所以要使r和t有实际意义的值,就必须加个取值符( * ),切记切记啊。
//另外set和multi set的区间都是左闭右开,所以t=*(--q.end())和q.erase((--q.end()))要把它-1操作,目的是取到它整个数列的最后一位。、
//set&&multiset(集合)
//这俩有啥区别呢:
//set是存入的元素是不能重复的,
//multiset是存入的元素是可以重复的。
//是不是感觉这两个板特别有用啊,再给几个操作吧:
//set <int> s;
//s.insert();
//s.erase();
//s.size();
//s.empty();
//所以set和multiset是很牛逼的,好好学吧!!!
//还是要求助STL,同时参考了《信息学奥赛一本通(C++版)》
//希望,水平提高后,能用C将此题编出。
//样例通过,提交AC 2017-12-22
#include <cstdio>
#include <set>
using namespace std;
multiset<int> q;
int main(){
int n,m,i,j,a,min,max;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&m);
for(j=1;j<=m;j++)
scanf("%d",&a),q.insert(a);
min=*q.begin(),max=*(--q.end());
printf("%d %d\n",min,max);
q.erase(q.begin()),q.erase(--q.end());
}
return 0;
}
//1373 鱼塘钓鱼(fishing)
//http://blog.csdn.net/Cliu__/article/details/77778623此文代码思路都写得不错,思路摘抄如下:
//首先由题意可知我们如果不在某个鱼塘钓鱼,那么这个鱼塘的鱼就不会少,显然在路上的花费是多余的,所以按照贪心的思路我们可以在一个鱼塘钓完鱼后再移动到下一个鱼塘.
//所以从左向右走,k枚举能到达的最远的鱼塘,然后开一个大根堆选出最多能选的鱼,当然别忘了每次减去路上的花费(只走一次)
//本人理解对样例如下:
//程序执行过程:
//1分钟 鱼塘3 钓鱼 20
//1分钟 鱼塘3 钓鱼 14
//1分钟 鱼塘2 钓鱼 14
//1分钟 鱼塘2 钓鱼 10
//1分钟 鱼塘1 钓鱼 10
//1分钟 鱼塘1 钓鱼 8
//走路时间 8分钟
//共计 14分钟 钓鱼 76
//实际钓鱼过程如下:
//1分钟 鱼塘1 钓鱼 10
//1分钟 鱼塘1 钓鱼 8
//3分钟 走到 鱼塘2
//1分钟 鱼塘2 钓鱼 14
//1分钟 鱼塘2 钓鱼 10
//5分钟 走到 鱼塘3
//1分钟 鱼塘3 钓鱼 20
//1分钟 鱼塘3 钓鱼 14
//共计 14分钟 钓鱼 76
//因N<100就不采用堆的写法了,直接枚举
//样例通过,提交AC 2018-1-1 15:01
#include <stdio.h>
#define maxn 110
int start[maxn],remain[maxn],cut[maxn],t[maxn];
int find_remain(int k){
int i,a,b=-999999999;
for(i=1;i<=k;i++)//找最多可钓鱼的鱼塘
if(b<=remain[i])//此处写成 if(b<remain[i])
b=remain[i],a=i;
return a;
}
int main(){
int i,j,k,n,T,time,d=0,ans,a,max=0;//d走路消耗的时间
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&start[i]);
for(i=1;i<=n;i++)scanf("%d",&cut[i]);
for(i=1;i<=n-1;i++)scanf("%d",&t[i]);
scanf("%d",&T);
for(k=1;k<=n;k++){//k个鱼塘进行枚举
ans=0,time=T-d;
for(i=1;i<=k;i++)remain[i]=start[i];
for(i=1;i<=k;i++){
a=find_remain(k);
while(time>0&&remain[a]>0){//有时间,且有鱼可钓
ans+=remain[a],remain[a]-=cut[a],time--;
a=find_remain(k);//漏了此句
}
}
d+=t[k];//此句写错位置,写到for(i=1;i<=k;i++)里面
max=max>ans?max:ans;
}
printf("%d",max);
return 0;
}
2018-1-1 15:01 AC 该节内容
2018-1-1 20:41 AC 该章节内容