Split Windows

题目链接:http://poj.org/problem?id=1108

题目大意:

给你一棵二叉树的先序遍历,有三种字符:|、-、A~Z,然后用窗口表示出来,|: 表示将当前窗口分为左右两半;-: 表示将当前窗口分为上下两半;A~Z: 为当前窗口命名。初始状态为一个大窗口,比如先序序列:|A-BC 表示,当然,形状是不长这样的,具体看样例,但大概意思就是这样。对于每一个小窗口,首先要做到最简,然后和旁边的窗口合并的时候要注意比例,如果出现小数,就左边的、上面的进一,右边的、下面的舍去小数。

题目解法:

并没有用到什么特殊的算法或者数据结构,只要懂得二叉树,接下来模拟即可,这道题做起来感觉挺有意思的。

一.     首先声明一个二叉树的数据结构

 typedef struct node     //树结点
{
char c; //保存该树结点的字符
int row,col; //保存该树结点的行和列,最后就是通过这个来绘制图形
struct node *fa; //父结点
struct node *lchild; //左孩子
struct node *rchild; //右孩子
}TreeNode;

二.     根据先序序列来构造这样的一根二叉树

 TreeNode *Creat(char *s,int &ls)    //根据 先序遍历 来构建这棵二叉树
{
TreeNode *T=Init(); //申请空间
T->c=s[ls++]; //赋值
if(s[ls-]=='-'||s[ls-]=='|') //如果这个结点不是叶子结点,就继续创建左右孩子
{
T->lchild=Creat(s,ls);
if(T->lchild) T->lchild->fa=T;
T->rchild=Creat(s,ls);
if(T->rchild) T->rchild->fa=T;
}
return T; //返回树结点的地址
}

三.     封装几个常用的函数

 TreeNode *Look_min(TreeNode *T)  //返回T子树的最左叶结点
{
while(T->lchild)
{
T=T->lchild;
}
return T;
} TreeNode *Look_max(TreeNode *T) //返回T子树的最右叶结点
{
while(T->rchild)
{
T=T->rchild;
}
return T;
} TreeNode *Look_fa(TreeNode *T) //查找结点T和T的前驱共同的祖先(第一个),如果T是第一个结点就返回NULL
{
while(T->fa)
{
if(T==T->fa->rchild)
{
return T->fa;
}
else T=T->fa;
}
return NULL;
} TreeNode *Look_pre(TreeNode *T) //查找结点T的前驱,如果没有前驱就返回NULL,这里说的前驱必须是叶结点
{
while(T->fa)
{
if(T==T->fa->rchild)
{
return Look_max(T->fa->lchild);
}
else T=T->fa;
}
return NULL;
}

四.     * 通过递归来计算每个大写字符应在的行和列

 void Calculate_H(TreeNode *T,int mh,int h,int maxh,int minh)
{
if(T)
{
Calculate_H(T->lchild,mh,h,maxh,minh);
if(T->c!='|'&&T->c!='-')
{
int height=h-T->row;
height=maxh*height/minh;
T->row=mh-height+;
}
Calculate_H(T->rchild,mh,h,maxh,minh);
}
} void Calculate_L(TreeNode *T,int ml,int l,int maxl,int minl)
{
if(T)
{
Calculate_L(T->lchild,ml,l,maxl,minl);
if(T->c!='|'&&T->c!='-')
{
int length=l-T->col;
length=maxl*length/minl;
T->col=ml-length+;
}
Calculate_L(T->rchild,ml,l,maxl,minl);
}
} void Calculate_HL(TreeNode *T)
{
if(T)
{
Calculate_HL(T->lchild);
if(T->c!='|'&&T->c!='-') //即叶子结点,就是大写字符
{
TreeNode *pre=Look_pre(T); //首先计算T结点的前驱
if(pre==NULL) //如果前驱为NULL,即第一个大写字母,这个点所在的行就是第一行,列是第一列
{
T->col=;
T->row=;
}
else //如果不是第一个字母
{
TreeNode *fa=Look_fa(T); //因为前面排除了第一个字母,所以这里肯定有前驱,也就肯定有和前驱共享的第一个父结点
if(fa->c=='|') //如果父结点的字符是'|'
{
T->row=Look_min(fa)->row; //那么他的行数和最左孩子是一个档次的,即都为这个被左右分成两半的窗口的第一行
T->col=fa->col+; //他的列在这个窗口中则是左边最大列数加2(中间有个-)
}
else
{
T->row=fa->row+; //同上,这里的每个结点保存的都是以他为根的子树的最大行数或者最大列数
/*
注意递归程序的执行,首先左子树,算完左子树就轮到根结点(中序遍历),然后更新根结点的行数为左子树的行数
就是下面那个else的执行内容,然后算右子树的时候,就可以直接使用根结点的行来表示左子树的最大行数,等右子树计
算完,再有后序遍历来重新更新根结点的值为两边的最大值
*/
T->col=Look_min(fa)->col;
}
}
}
else
{
T->col=T->lchild->col; //更新根结点的行和列,注意这里遇到的只会是'|'或'-'
T->row=T->lchild->row;
}
Calculate_HL(T->rchild);
if(T->c=='|'||T->c=='-')
{
T->row=T->row>T->rchild->row?T->row:T->rchild->row; //这个就是后序遍历更新根结点的位置
T->col=T->col>T->rchild->col?T->col:T->rchild->col;
/*
最后,如果这个根结点的字符是'|',就是左右分开这个窗口,那么要计算两边的行,按比例更新行的大小,如果是'-',同样
也要更新上下两个子窗口的列
*/
if(T->c=='|')
{
int mr_lchild=T->lchild->row; //左子树的最大行数
int mr_rchild=T->rchild->row; //右子树的最大行数
if(mr_lchild>mr_rchild) //右子树比较低,那么要按照比例拉伸右子树
{
int mh=mr_lchild+; //先将行转化为高度,下面是细节。
int h=mr_rchild+;
int maxh=mh-Look_min(T->lchild)->row+;
int minh=h-Look_min(T->rchild)->row;
Calculate_H(T->rchild,mh,h,maxh,minh);
}
else
{
int mh=mr_rchild+;
int h=mr_lchild+;
int maxh=mh-Look_min(T->lchild)->row+;
int minh=h-Look_min(T->rchild)->row;
Calculate_H(T->lchild,mh,h,maxh,minh);
}
}
else //同上
{
int mc_lchild=T->lchild->col;
int mc_rchild=T->rchild->col;
if(mc_lchild>mc_rchild)
{
int ml=mc_lchild+;
int l=mc_rchild+;
int maxl=ml-Look_min(T->lchild)->col+;
int minl=l-Look_min(T->rchild)->col;
Calculate_L(T->rchild,ml,l,maxl,minl);
}
else
{
int ml=mc_rchild+;
int l=mc_lchild+;
int maxl=ml-Look_min(T->lchild)->col+;
int minl=l-Look_min(T->rchild)->col;
Calculate_L(T->lchild,ml,l,maxl,minl);
}
}
}
}
}

五.     通过递归来画出窗口基本的骨架

 char tu[][];           //保存最终的图

 void Draw(TreeNode *T)
{
if(T)
{
Draw(T->lchild);
if(T->c!='|'&&T->c!='-') //如果是字母的话,那么在那一行,那一列,填上字母
{
tu[T->row][T->col]=T->c;
}
else if(T->c=='|') //如果左右分的话,那么在右窗口所属的那个字母的列上画'|',起点h1,终点h2
{
TreeNode *tmp=Look_min(T->rchild); //右窗口所属的那一个字母就是右子树的最左孩子
int h1=tmp->row+,h2=h1; //起点就是这个字母的下一格,先假设终点等于起点
TreeNode *fa=T->fa; //现在要找的是这个结点的祖先并且是'-'(并且这个结点是在他的左子树),这样,他右孩子的最左孩子就是目标
if(!fa) h2=T->row; //特殊情况处理,如果为NULL,那么这一竖就是直接竖到底的,就是T的行数
bool flag; //标记结点是他的父结点的左孩子还是右孩子,0 是左孩子,1是右孩子
if(fa&&fa->lchild==T) flag=; //下面就是找目标的过程
else flag=;
while(fa)
{
if(fa->c=='-'&&flag==)
{
h2=Look_min(fa->rchild)->row;
break;
}
else
{
h2=fa->row;
}
if(fa->fa&&fa->fa->lchild==fa) flag=;
else flag=;
fa=fa->fa;
}
while(h1<h2) //确定了起点和终点,就可以开始画线了
{
tu[h1++][tmp->col]='|';
}
/*
下面过程不能直接忽略,因为如果*都留到最后处理,会很麻烦。
*/
if(tu[h1][tmp->col]<='Z'&&tu[h1][tmp->col]>='A'); //竖到头后,那个点一般是*,或者有可能遇到字母,就忽略
else
{
tu[h1][tmp->col]='*';
}
}
else //同上
{
TreeNode *tmp=Look_min(T->rchild);
int l1=tmp->col+,l2=l1;
TreeNode *fa=T->fa;
if(!fa) l2=T->col;
bool flag;
if(fa&&fa->lchild==T) flag=;
else flag=;
while(fa)
{
if(fa->c=='|'&&flag==)
{
l2=Look_min(fa->rchild)->col;
break;
}
else
{
l2=fa->col;
}
if(fa->fa&&fa->fa->lchild==fa) flag=;
else flag=;
fa=fa->fa;
}
while(l1<l2)
{
tu[tmp->row][l1++]='-';
}
if( tu[tmp->row][l1]<='Z'&& tu[tmp->row][l1]>='A');
else
{
tu[tmp->row][l1]='*';
}
}
Draw(T->rchild);
}
}

六.     修补这个图

 void Fix(TreeNode *T)        //修补残图
{
//首先是边界线
for(int i=;i<=T->col;i++)
{
if(tu[][i]==)
{
tu[][i]='-';
if(tu[][i]!=) tu[][i]='*';
}
if(tu[T->row][i]==)
{
tu[T->row][i]='-';
if(tu[T->row-][i]!=) tu[T->row][i]='*';
}
}
for(int i=;i<=T->row;i++)
{
if(tu[i][]==)
{
tu[i][]='|';
if(tu[i][]!=) tu[i][]='*';
}
if(tu[i][T->col]==)
{
tu[i][T->col]='|';
if(tu[i][T->col-]!=) tu[i][T->col]='*';
}
}
//然后是四个顶点
if(tu[][T->col]<='Z'&&tu[][T->col]>='A');
else tu[][T->col]='*';
if(tu[][]<='Z'&&tu[][]>='A');
else tu[][]='*';
if(tu[T->row][]<='Z'&&tu[T->row][]>='A');
else tu[T->row][]='*';
if(tu[T->row][T->col]<='Z'&&tu[T->row][T->col]>='A');
else tu[T->row][T->col]='*';
//最后是有交叉的地方,要变成*号
for(int i=;i<=T->row;i++)
{
for(int j=;j<=T->col;j++)
{
if(tu[i][j]!=&&(tu[i][j]<'A'||tu[i][j]>'Z'))
{
int co=;
if(tu[i-][j]!=) co++;
if(tu[i][j-]!=) co++;
if(tu[i+][j]!=) co++;
if(tu[i][j+]!=) co++;
if(co>=) tu[i][j]='*';
}
}
}
}

  注意在主函数中,计算完行列以后,要先将根结点的行列都加2(除非他直接是字母),因为边框的原因。

  

 #include<stdio.h>
#include<string.h>
#include<stdlib.h> typedef struct node //树结点
{
char c; //保存该树结点的字符
int row,col; //保存该树结点的行和列,最后就是通过这个来绘制图形
struct node *fa; //父结点
struct node *lchild; //左孩子
struct node *rchild; //右孩子
}TreeNode; TreeNode *Init() //初始化树结点
{
TreeNode *x;
x=(TreeNode *)malloc(sizeof(TreeNode));
x->fa=NULL;
x->row=x->col=;
x->lchild=NULL;
x->rchild=NULL;
return x;
} TreeNode *Creat(char *s,int &ls) //根据 先序遍历 来构建这棵二叉树
{
TreeNode *T=Init(); //申请空间
T->c=s[ls++]; //赋值
if(s[ls-]=='-'||s[ls-]=='|') //如果这个结点不是叶子结点,就继续创建左右孩子
{
T->lchild=Creat(s,ls);
if(T->lchild) T->lchild->fa=T;
T->rchild=Creat(s,ls);
if(T->rchild) T->rchild->fa=T;
}
return T; //返回树结点的地址
} void Dis(TreeNode *T) //查看生成的二叉树所用。(先序遍历)
{
if(T)
{
if(T->lchild==NULL)
{
printf("自己:%c 行数:%d 列数:%d\n",T->c,T->row,T->col);
}
Dis(T->lchild);
Dis(T->rchild);
}
} void Clear(TreeNode *T) //收回动态申请的空间
{
if(T)
{
Clear(T->lchild);
Clear(T->rchild);
free(T);
}
} TreeNode *Look_min(TreeNode *T) //返回T子树的最左叶结点
{
while(T->lchild)
{
T=T->lchild;
}
return T;
} TreeNode *Look_max(TreeNode *T) //返回T子树的最右叶结点
{
while(T->rchild)
{
T=T->rchild;
}
return T;
} TreeNode *Look_fa(TreeNode *T) //查找结点T和T的前驱共同的祖先(第一个),如果T是第一个结点就返回NULL
{
while(T->fa)
{
if(T==T->fa->rchild)
{
return T->fa;
}
else T=T->fa;
}
return NULL;
} TreeNode *Look_pre(TreeNode *T) //查找结点T的前驱,如果没有前驱就返回NULL,这里说的前驱必须是叶结点
{
while(T->fa)
{
if(T==T->fa->rchild)
{
return Look_max(T->fa->lchild);
}
else T=T->fa;
}
return NULL;
} void Calculate_H(TreeNode *T,int mh,int h,int maxh,int minh)
{
if(T)
{
Calculate_H(T->lchild,mh,h,maxh,minh);
if(T->c!='|'&&T->c!='-')
{
int height=h-T->row;
height=maxh*height/minh;
T->row=mh-height+;
}
Calculate_H(T->rchild,mh,h,maxh,minh);
}
} void Calculate_L(TreeNode *T,int ml,int l,int maxl,int minl)
{
if(T)
{
Calculate_L(T->lchild,ml,l,maxl,minl);
if(T->c!='|'&&T->c!='-')
{
int length=l-T->col;
length=maxl*length/minl;
T->col=ml-length+;
}
Calculate_L(T->rchild,ml,l,maxl,minl);
}
} void Calculate_HL(TreeNode *T)
{
if(T)
{
Calculate_HL(T->lchild);
if(T->c!='|'&&T->c!='-') //即叶子结点,就是大写字符
{
TreeNode *pre=Look_pre(T); //首先计算T结点的前驱
if(pre==NULL) //如果前驱为NULL,即第一个大写字母,这个点所在的行就是第一行,列是第一列
{
T->col=;
T->row=;
}
else //如果不是第一个字母
{
TreeNode *fa=Look_fa(T); //因为前面排除了第一个字母,所以这里肯定有前驱,也就肯定有和前驱共享的第一个父结点
if(fa->c=='|') //如果父结点的字符是'|'
{
T->row=Look_min(fa)->row; //那么他的行数和最左孩子是一个档次的,即都为这个被左右分成两半的窗口的第一行
T->col=fa->col+; //他的列在这个窗口中则是左边最大列数加2(中间有个-)
}
else
{
T->row=fa->row+; //同上,这里的每个结点保存的都是以他为根的子树的最大行数或者最大列数
/*
注意递归程序的执行,首先左子树,算完左子树就轮到根结点(中序遍历),然后更新根结点的行数为左子树的行数
就是下面那个else的执行内容,然后算右子树的时候,就可以直接使用根结点的行来表示左子树的最大行数,等右子树计
算完,再有后序遍历来重新更新根结点的值为两边的最大值
*/
T->col=Look_min(fa)->col;
}
}
}
else
{
T->col=T->lchild->col; //更新根结点的行和列,注意这里遇到的只会是'|'或'-'
T->row=T->lchild->row;
}
Calculate_HL(T->rchild);
if(T->c=='|'||T->c=='-')
{
T->row=T->row>T->rchild->row?T->row:T->rchild->row; //这个就是后序遍历更新根结点的位置
T->col=T->col>T->rchild->col?T->col:T->rchild->col;
/*
最后,如果这个根结点的字符是'|',就是左右分开这个窗口,那么要计算两边的行,按比例更新行的大小,如果是'-',同样
也要更新上下两个子窗口的列
*/
if(T->c=='|')
{
int mr_lchild=T->lchild->row; //左子树的最大行数
int mr_rchild=T->rchild->row; //右子树的最大行数
if(mr_lchild>mr_rchild) //右子树比较低,那么要按照比例拉伸右子树
{
int mh=mr_lchild+; //先将行转化为高度,下面是细节。
int h=mr_rchild+;
int maxh=mh-Look_min(T->lchild)->row+;
int minh=h-Look_min(T->rchild)->row;
Calculate_H(T->rchild,mh,h,maxh,minh);
}
else
{
int mh=mr_rchild+;
int h=mr_lchild+;
int maxh=mh-Look_min(T->lchild)->row+;
int minh=h-Look_min(T->rchild)->row;
Calculate_H(T->lchild,mh,h,maxh,minh);
}
}
else //同上
{
int mc_lchild=T->lchild->col;
int mc_rchild=T->rchild->col;
if(mc_lchild>mc_rchild)
{
int ml=mc_lchild+;
int l=mc_rchild+;
int maxl=ml-Look_min(T->lchild)->col+;
int minl=l-Look_min(T->rchild)->col;
Calculate_L(T->rchild,ml,l,maxl,minl);
}
else
{
int ml=mc_rchild+;
int l=mc_lchild+;
int maxl=ml-Look_min(T->lchild)->col+;
int minl=l-Look_min(T->rchild)->col;
Calculate_L(T->lchild,ml,l,maxl,minl);
}
}
}
}
} char tu[][]; //保存最终的图 void Draw(TreeNode *T)
{
if(T)
{
Draw(T->lchild);
if(T->c!='|'&&T->c!='-') //如果是字母的话,那么在那一行,那一列,填上字母
{
tu[T->row][T->col]=T->c;
}
else if(T->c=='|') //如果左右分的话,那么在右窗口所属的那个字母的列上画'|',起点h1,终点h2
{
TreeNode *tmp=Look_min(T->rchild); //右窗口所属的那一个字母就是右子树的最左孩子
int h1=tmp->row+,h2=h1; //起点就是这个字母的下一格,先假设终点等于起点
TreeNode *fa=T->fa; //现在要找的是这个结点的祖先并且是'-'(并且这个结点是在他的左子树),这样,他右孩子的最左孩子就是目标
if(!fa) h2=T->row; //特殊情况处理,如果为NULL,那么这一竖就是直接竖到底的,就是T的行数
bool flag; //标记结点是他的父结点的左孩子还是右孩子,0 是左孩子,1是右孩子
if(fa&&fa->lchild==T) flag=; //下面就是找目标的过程
else flag=;
while(fa)
{
if(fa->c=='-'&&flag==)
{
h2=Look_min(fa->rchild)->row;
break;
}
else
{
h2=fa->row;
}
if(fa->fa&&fa->fa->lchild==fa) flag=;
else flag=;
fa=fa->fa;
}
while(h1<h2) //确定了起点和终点,就可以开始画线了
{
tu[h1++][tmp->col]='|';
}
/*
下面过程不能直接忽略,因为如果*都留到最后处理,会很麻烦。
*/
if(tu[h1][tmp->col]<='Z'&&tu[h1][tmp->col]>='A'); //竖到头后,那个点一般是*,或者有可能遇到字母,就忽略
else
{
tu[h1][tmp->col]='*';
}
}
else //同上
{
TreeNode *tmp=Look_min(T->rchild);
int l1=tmp->col+,l2=l1;
TreeNode *fa=T->fa;
if(!fa) l2=T->col;
bool flag;
if(fa&&fa->lchild==T) flag=;
else flag=;
while(fa)
{
if(fa->c=='|'&&flag==)
{
l2=Look_min(fa->rchild)->col;
break;
}
else
{
l2=fa->col;
}
if(fa->fa&&fa->fa->lchild==fa) flag=;
else flag=;
fa=fa->fa;
}
while(l1<l2)
{
tu[tmp->row][l1++]='-';
}
if( tu[tmp->row][l1]<='Z'&& tu[tmp->row][l1]>='A');
else
{
tu[tmp->row][l1]='*';
}
}
Draw(T->rchild);
}
} void Fix(TreeNode *T) //修补残图
{
//首先是边界线
for(int i=;i<=T->col;i++)
{
if(tu[][i]==)
{
tu[][i]='-';
if(tu[][i]!=) tu[][i]='*';
}
if(tu[T->row][i]==)
{
tu[T->row][i]='-';
if(tu[T->row-][i]!=) tu[T->row][i]='*';
}
}
for(int i=;i<=T->row;i++)
{
if(tu[i][]==)
{
tu[i][]='|';
if(tu[i][]!=) tu[i][]='*';
}
if(tu[i][T->col]==)
{
tu[i][T->col]='|';
if(tu[i][T->col-]!=) tu[i][T->col]='*';
}
}
//然后是四个顶点
if(tu[][T->col]<='Z'&&tu[][T->col]>='A');
else tu[][T->col]='*';
if(tu[][]<='Z'&&tu[][]>='A');
else tu[][]='*';
if(tu[T->row][]<='Z'&&tu[T->row][]>='A');
else tu[T->row][]='*';
if(tu[T->row][T->col]<='Z'&&tu[T->row][T->col]>='A');
else tu[T->row][T->col]='*';
//最后是有交叉的地方,要变成*号
for(int i=;i<=T->row;i++)
{
for(int j=;j<=T->col;j++)
{
if(tu[i][j]!=&&(tu[i][j]<'A'||tu[i][j]>'Z'))
{
int co=;
if(tu[i-][j]!=) co++;
if(tu[i][j-]!=) co++;
if(tu[i+][j]!=) co++;
if(tu[i][j+]!=) co++;
if(co>=) tu[i][j]='*';
}
}
}
} int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w+",stdout);
TreeNode *T;
char s[];
int t,g=;
scanf("%d",&t);
while(t--)
{
printf("%d\n",g++);
scanf("%s",s);
int ls=;
T=Creat(s,ls);
Calculate_HL(T);
memset(tu,,sizeof(tu));
if(T->lchild!=NULL)
{
T->row+=;
T->col+=;
Draw(T);
Fix(T);
for(int i=;i<=T->row;i++)
{
for(int j=;j<=T->col;j++)
{
printf("%c",tu[i][j]==?' ':tu[i][j]);
}
printf("\n");
}
}
else
{
printf("%c-*\n",T->c);
printf("| |\n");
printf("*-*\n");
}
Clear(T);
}
return ;
}

AC代码

POJ1108_Split Windows 解题报告的更多相关文章

  1. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

  2. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...

  3. BZOJ 1051 最受欢迎的牛 解题报告

    题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[S ...

  4. 习题:codevs 2822 爱在心中 解题报告

    这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...

  5. 习题:codevs 1035 火车停留解题报告

    本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...

  6. 习题: codevs 2492 上帝造题的七分钟2 解题报告

    这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...

  7. 习题:codevs 1519 过路费 解题报告

    今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...

  8. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

  9. LeetCode 解题报告索引

    最近在准备找工作的算法题,刷刷LeetCode,以下是我的解题报告索引,每一题几乎都有详细的说明,供各位码农参考.根据我自己做的进度持续更新中......                        ...

随机推荐

  1. tushare 开源数据包的使用

    tushare 使用 python开源金融接口包: tushare.org/trading.html#d2 安装: pip install tushare import tushare as ts # ...

  2. Django 中的 日志处理

    日志处理: 上线后必须使用 便于以后的 维护 管理 根据日志 处理 BUG 在 项目中 定义一个 存放日志的 文件夹 log 存放所有 等级 的 日志 配置: 将下面的日志的 配置 写入 django ...

  3. 8.04-book

    import requests from lxml import etree from bs4 import BeautifulSoup import json class BookSpider(ob ...

  4. SQlite源码分析-体系结构

    体系结构 在内部,SQLite由以下几个组件组成:内核.SQL编译器.后端以及附件.SQLite通过利用虚拟机和虚拟数据库引擎(VDBE),使调试.修改和扩展SQLite的内核变得更加方便.所有SQL ...

  5. rabbitmq官方的六种工作模式

    1.RabbitMq1.1介绍RabbitMQ是一个消息代理:它接受并转发消息.你可以把它当成一个邮局:当你想邮寄信件的时候,你会把信件放在投递箱中,并确信邮递员最终会将信件送到收件人的手里.在这个例 ...

  6. direct path read temp的处理方法

    Examine the SQL statement currently being run by the session experiencing waits to see what is causi ...

  7. oracle 乘积的实现方法

    with abc(col1) as ( ' from dual union all ' from dual union all ' from dual ) select col1,ln(col1),e ...

  8. 修复XSS跨站漏洞

    XSS跨站漏洞最终形成的原因是对输入与输出没有严格过滤. 1.输入与输出 在HTML中,<,>,",',&都有比较特殊的意义.HTML标签,属性就是由这几个符合组成的.P ...

  9. IDEA+TestNG

    1.常用注解 2.手把手教你掌握必备测试框架testNG:http://www.51testing.com/zhuanti/TestNG.htm [testNG常用注解] 1 @Test:标记一个类或 ...

  10. 从code review到Git commit log

    最近在读一本技术类的书:朱赟——<跃迁:从技术到管理的硅谷路径>,其中聊了很多很有趣的观点,比如:技术管理.技术实践.硅谷文化.个人成长等. 读到关于硅谷人如何做code review这一 ...