20160702 自勉

  • 钻研那些最需要脑力的事。

  • 去 SJTU ,去魔都,去遇见更大的世界。

  • 高考可以重来;而 OI ,无论是 Happy Endding 还是 Sad Endding ,真的没有 One More Chance 了。

  • 没有一场胜利的战役是按计划进行的,但是没有计划是打不赢胜仗的。

向上吧,少年!

分享到 评论

NOI OpenJudge 题库 8211 派

描述

我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。

我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。

请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。

输入

第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。

输出

输出每个人能得到的最大的派的体积,精确到小数点后三位。

样例输入

1
2
3 3
4 3 3

样例输出

1
25.133

分析

先由半径求体积,而后二分查找,区间为(0,最大的单个派的体积)。

个人 WA 的原因:

  • π 的值一开始取 3.141569 ,小数点后 6 位,这样的精度居然还不够,可以用 math 库中的反余弦函数 acos(-1.0)
  • check 函数中表示当前能够分的块数的变量 sum 要用 int

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#define loop(i,n) for(int i=0;i<n;i++)
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n,f;
double a[10010];
const double PI=acos(-1.0);
int check(double x)
{
int sum=0;
loop(i,n)
{
sum+=a[i]/x;
if(sum>=f)
return 1;
}
return 0;
}
int main()
{
double left=0,right=0,mid;
cin >>n>>f;
f++;
loop(i,n)
{
cin >>a[i];
a[i]=a[i]*a[i]*PI;
if(a[i]>right)
right=a[i];
}
while(right - left>0.000001)
{
mid=(left+right)/2;
if(check(mid))
left=mid;
else right=mid;
}
printf("%.3lf\n",left);
return 0;
}
分享到 评论

NOI OpenJudge 题库 7623 五户共井问题

描述

有A, B, C, D, E五家人共用一口井,已知井深不超过k米。A, B, C, D, E的绳长各不相同,而且厘米表示的绳长一定是整数。
从井口放下绳索正好达到水面时:
(a)需要A家的绳n1条接上B家的绳1条
(b)需要B家的绳n2条接上C家的绳1条
(c)需要C家的绳n3条接上D家的绳1条
(d)需要D家的绳n4条接上E家的绳1条
(e)需要E家的绳n5条接上A家的绳1条
问井深和各家绳长。

输入

输入只有1行。包括空格分开的6个整数。
第一个整数k(1 <= k <= 20),代表井的最大深度(单位:米)。
接下来是5个正整数n1, n2, n3, n4, n5。这五个整数的含义见上面的题目描述。

输出

输出只有1行。
如果找到了可行解,就输出6个整数,用空格分开,分别代表井的深度和A, B, C, D, E的绳长(单位都是厘米)。
如果有多组可行解,输出井的深度最小的那组解。
如果不存在可行解,就输出一行:
not found

样例输入

1
10 2 3 4 5 6

样例输出

1
721 265 191 148 129 76

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<iostream>
using namespace std;
int main()
{
int k;
int n1,n2,n3,n4,n5;
cin >>k>>n1>>n2>>n3>>n4>>n5;
int H,A,B,C,D,E;
int ANSH=2147483647,ANSA,ANSB,ANSC,ANSD,ANSE;
for(A=1;A<=k*100;A++)
for(B=1;B<=k*100;B++)
{
H = A * n1 + B;
C = H - B * n2;
D = H - C * n3;
E = H - D * n4;
if((E * n5 + A != H) || H>k*100 || C<=0 || D<=0 || E<=0 || A==B || A==C || A==D || A==E || B==C || B==D || B==E || C==D || C==E ||D==E)
continue;
if(H<ANSH)
{
ANSH=H;
ANSA=A;
ANSB=B;
ANSC=C;
ANSD=D;
ANSE=E;
}
}
if (ANSH==2147483647)
cout <<"not found"<<endl;
else cout <<ANSH<<" "<<ANSA<<" "<<ANSB<<" "<<ANSC<<" "<<ANSD<<" "<<ANSE<<endl;
return 0;
}
分享到 评论

NOI OpenJudge 题库 7914 分数线划定

描述

世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才,A市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的150%划定,即如果计划录取m名志愿者,则面试分数线为排名第m*150%(向下取整)名的选手的分数,而最终进入面试的选手为笔试成绩不低于面试分数线的所有选手。

现在就请你编写程序划定面试分数线,并输出所有进入面试的选手的报名号和笔试成绩。

输入

第一行,两个整数n,m(5 ≤ n ≤ 5000,3 ≤ m ≤ n),中间用一个空格隔开,其中n 表示报名参加笔试的选手总数,m 表示计划录取的志愿者人数。输入数据保证m*150%向下取整后小于等于n。
第二行到第 n+1 行,每行包括两个整数,中间用一个空格隔开,分别是选手的报名号k(1000 ≤ k ≤ 9999)和该选手的笔试成绩s(1 ≤ s ≤ 100)。数据保证选手的报名号各不相同。

输出

第一行,有两个整数,用一个空格隔开,第一个整数表示面试分数线;第二个整数为进入面试的选手的实际人数。
从第二行开始,每行包含两个整数,中间用一个空格隔开,分别表示进入面试的选手的报名号和笔试成绩,按照笔试成绩从高到低输出,如果成绩相同,则按报名号由小到大的顺序输出。

样例输入

1
2
3
4
5
6
7
6 3
1000 90
3239 88
2390 95
7231 84
1005 95
1001 88

样例输出

1
2
3
4
5
6
88 5
1005 95
2390 95
1000 90
1001 88
3239 88

提示

样例说明:m150% = 3150% = 4.5,向下取整后为4。保证4个人进入面试的分数线为88,但因为88有重分,所以所有成绩大于等于88的选手都可以进入面试,故最终有5个人进入面试。

分析

sort() 函数很强大,需要注意的是 cmp 的写法。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#define loop(i,n) for(int i=0;i<n;i++)
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int number,score;
}a[10010];
bool cmp(node a,node b)
{
if(a.score==b.score)
return a.number<b.number;
return a.score>b.score;
}
int main()
{
int n,m;
cin >>n>>m;
m=int(m*1.5);
loop(i,10010)
a[i].score=0;
loop(i,n)
cin >>a[i].number>>a[i].score;
sort(a,a+10010,cmp);
int j=m;
int count=m;
while(a[j].score==a[m-1].score)
{
count++;
j++;
}
cout <<a[m-1].score<<" "<<count<<endl;
loop(i,m)
cout <<a[i].number<<" "<<a[i].score<<endl;
j=m;
while(a[j].score==a[m-1].score)
{
cout <<a[j].number<<" "<<a[j].score<<endl;
j++;
}
return 0;
}
分享到 评论

NOI OpenJudge 题库 1818 红与黑

描述

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

输入

包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下
1)‘.’:黑色的瓷砖;
2)‘#’:白色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。

输出

对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

样例输入

1
2
3
4
5
6
7
8
9
10
11
6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
0 0

样例输出

1
45

分析

深搜入门题,开始以为是求最大联通块,仔细看发现只要求源点所在的连通块大小。居然没有数据范围,于是我就开了 1100*1100 水过了。另外有一个注意点 -> 给的 W / H 是 列 / 行 ,而正常题目是 行 / 列,循环需要把 W / H 倒过来。

似乎也可以用宽搜写,有空填坑。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#define loop(i,n) for(int i=0;i<n;i++)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int w,h;
int ans;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
char tile[1100][1100];
int map[1100][1100];
bool flag[1100][1100];
struct node
{
int x,y;
}start;
bool check(int x,int y)
{
if(!flag[x][y] && map[x][y] && x>=0 && x<h && y>=0 && y<w)
return 1;
return 0;
}
void dfs(int x,int y)
{
ans++;
flag[x][y]=1;
loop(i,4)
{
int curx=x+dx[i],cury=y+dy[i];
if(check(curx,cury))
dfs(curx,cury);
}
}
int main()
{
while(scanf("%d%d",&w,&h)==2 && w && h)
{
ans=0;
memset(map,0,sizeof(map));
memset(flag,0,sizeof(flag));
loop(i,h)
loop(j,w)
{
cin >>tile[i][j];
if(tile[i][j]=='.')
map[i][j]=1;
if(tile[i][j]=='#')
map[i][j]=0;
if(tile[i][j]=='@')
{
map[i][j]=1;
flag[i][j]=1;
start.x=i;
start.y=j;
}
}
dfs(start.x,start.y);
cout <<ans<<endl;
}
return 0;
}
分享到 评论

NOI OpenJudge 题库 6264 走出迷宫

描述

当你站在一个迷宫里的时候,往往会被错综复杂的道路弄得失去方向感,如果你能得到迷宫地图,事情就会变得非常简单。
假设你已经得到了一个n*m的迷宫的图纸,请你找出从起点到出口的最短路。

输入

第一行是两个整数n和m(1<=n,m<=100),表示迷宫的行数和列数。
接下来n行,每行一个长为m的字符串,表示整个迷宫的布局。字符’.’表示空地,’#’表示墙,’S’表示起点,’T’表示出口。

输出

输出从起点到出口最少需要走的步数。

样例输入

1
2
3
4
3 3
S#T
.#.
...

样例输出

1
6

分析

最近在刷 OpenJudge ,怎么说也得拿个 200 分换省三呢。

宽搜入门题,试了一把 STL 队列,写完觉得链表、栈、队列这种东西还是自己实现算了 …… 这些用 STL 写实在太花哨,也并没有方便太多,但是 STL 的优先队列、堆倒还是很有用的。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#define loop(i,n) for(int i=0;i<n;i++)
const int INF=2147483647;
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
char maze[110][110];
int dis[110][110];
bool map[110][110];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
typedef pair<int,int> matrix;
matrix start,end;
queue<matrix> q;
int n,m;
void init()
{
memset(map,0,sizeof(map));
loop(i,110)
loop(j,110)
dis[i][j]=INF;
cin >>n>>m;
loop(i,n)
loop(j,m)
{
cin >>maze[i][j];
if(maze[i][j]=='S')
{
start.first=i;
start.second=j;
map[i][j]=0;
}
if(maze[i][j]=='T')
{
end.first=i;
end.second=j;
map[i][j]=0;
}
if(maze[i][j]=='.')
map[i][j]=0;
if(maze[i][j]=='#')
map[i][j]=1;
}
}
int check(int x,int y)
{
if(dis[x][y]==INF && !map[x][y] && x>=0 && x<n && y>=0 && y<m)
return 1;
return 0;
}
int bfs()
{
dis[start.first][start.second]=0;
q.push(matrix(start.first,start.second));
while(q.size())
{
matrix tmp=q.front();
q.pop();
if(tmp.first==end.first && tmp.second==end.second)
break;
loop(i,4)
{
int curx=tmp.first+dx[i],cury=tmp.second+dy[i];
if(check(curx,cury))
{
q.push(matrix(curx,cury));
dis[curx][cury]=dis[tmp.first][tmp.second]+1;
}
}
}
return dis[end.first][end.second];
}
int main()
{
init();
cout <<bfs()<<endl;
return 0;
}
分享到 评论

递推、搜索、贪心和动态规划的区别

递推:每个阶段只有一个状态;

搜索:每个阶段的最优状态是由之前所有阶段的状态的组合得到的;

贪心:每个阶段的最优状态都是由上一个阶段的最优状态得到的;

动态规划:每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到(最优子结构),而不管之前这个状态是如何得到的(无后效性)。

分享到 评论

收入计划

问题描述

高考结束后,同学们大都找到了一份临时工作,渴望挣得一些零用钱。从今天起,Matrix67将连续工作N天(1<=N<=100 000)。每一天末他可以领取当天及前面若干天里没有领取的工资,但他总共只有M(1<=M<=N)次领取工资的机会。Matrix67已经知道了在接下来的这N天里每一天他可以赚多少钱。为了避免自己滥用零花钱,他希望知道如何安排领取工资的时间才能使得领到工资最多的那一次工资数额最小。注意Matrix67必须恰好领工资M次,且需要将所有的工资全部领走(即最后一天末需要领一次工资)。

输入

第一行输入两个用空格隔开的正整数N和M
以下N行每行一个不超过10000正整数,依次表示每一天的薪水。

输出

输出领取到的工资的最大值最小是多少。

样例输入

1
2
3
4
5
6
7
8
7 5
100
400
300
100
500
101
400

样例输出

1
500

样例说明

采取下面的方案可以使每次领到的工资不会多于500。这个答案不能再少了。
100 400 300 100 500 101 400 每一天的薪水
<——1 <——-2 <—3 <—4 <—5 领取工资的时间
500 400 500 101 400 领取到的工资

分析

最小值最大/最大值最小类问题 -> 二分答案典型问题

二分答案模板,对于 l 与 r 的理解,不要仅仅用「二分法解方程」的思想理解,此类题 可把 l 看作不可行域, r 看作可行域

此题需要二分工资最大值(单次领取的最高工资必定在 [单日工资最大值,全部工资] 的区间内)利用贪心(从左到右能取就取),判断是否可行。

二分答案模板

1
2
3
4
5
6
7
8
9
10
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# include<iostream>
using namespace std;
int n,m,sum=0,maximum=-1,ans;
int a[101000];
int check(int x)
{
int i;
int times=0,tot=0;
for(i=1;i<=n;i++)
{
tot=tot+a[i];
if(tot>x)
{
tot=a[i];
times++;
}
if(times+1>m)//判断是否恰好领 m 次,注意是 times+1
return 0;
}
return 1;
}
int main()
{
int i;
cin >>n>>m;
for(i=1;i<=n;i++)
{
cin >>a[i];
maximum=max(maximum,a[i]);
sum=sum+a[i];
}
int l=maximum,r=sum;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
cout <<ans<<endl;
return 0;
}
分享到 评论

最短路径问题

问题描述

平面上有 n 个点(n<=100),每个点的坐标均在-10000~10000 之间。其中的一些点之间有连线 。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离 。现在的任务是找出从一点到另一点之间的最短路径 。

输入

输入共 n+m+3 行,其中:
第一行为整数 n。
第 2 行到第 n+1 行 (共 n 行 ),每行两个整数 x 和 y,描述了一个点的坐标。
第 n+2 行为一个整数 m,表示图中连线 的个数。
此后的 m 行(m<=1000),每行描述一条连线,由两个整数 i和 j 组成,表示第 i个点和第 j 个点之
间有连线 。
最后一行 :两个整数 s 和 t,分别表示源点和目标点。

输出

输出仅一行,一个实数(保留两位小数),表示从 s 到 t 的最短路径长度 。

样例输入

1
5 0 0 2 0 2 2 0 2 3 1 5 1 2 1 3 1 4 2 5 3 5 1 5

样例输出

1
3.41

分析

顶点数较小,不加优化的 Dijkstra 即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const int INF=1<<29;
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int x[110],y[110];
double dist[110][110];
// dist 数组即用作存边权的边集数组,若两点间有边则存其边权,没有则赋为 ∞
bool flag[110];// flag 用作标记每个端点是否已访问
double getdist(int x1,int x2,int y1,int y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
memset(flag,0,sizeof(flag));
int n,m,s,t;
int i,j,k,pos;
for(i=0;i<110;i++)
{
flag[i]=0;
for(j=0;j<110;j++)
dist[i][j]=INF;
}
cin >>n;
for(i=1;i<=n;i++)
{
dist[i][i]=0;
cin >>x[i]>>y[i];
}
cin >>m;
for(i=1;i<=m;i++)
{
int a,b;
cin >>a>>b;
dist[a][b]=dist[b][a]=getdist(x[a],x[b],y[a],y[b]);
}
cin >>s>>t;
flag[s]=1;
for(i=1;i<=n;i++)
{
int minimum=INF;
for(j=1;j<=n;j++)//找离 s 最近点 j
{
if(!flag[j] && dist[s][j]<minimum)
{
pos=j;
minimum=dist[s][j];
}
}
flag[pos]=1;
for(j=1;j<=n;j++)
if(!flag[j] && dist[s][pos]+dist[pos][j]<dist[s][j])
//以找到的点为中间点,更新 s 到各顶点的最短距离
dist[s][j]=dist[s][pos]+dist[pos][j];
}
printf("%.2lf\n",dist[s][t]);
return 0;
}
分享到 评论

如何在 VPS 上搭建 Hexo 博客并使用 Git Hooks 更新?

几个你或许会问我的问题

为什么是 Hexo ?

A fast, simple & powerful blog framework, powered by Node.js.

- @tommy351 ( Hexo 作者 )

现在是 2015 年,身在开发者圈里,如果想开始写一个 Blog ,使用静态网站生成器搭建无疑是最好的选择,而最热门的三款生成器 Jekyll / Octopress / Hexo 中,对于不懂前端的我们, Hexo 有着质量最高的主题,几乎可以说是唯一的选择,相比 Jekyll / Octopress ,安装简便,生成页面效率有质的区别(可以想象某一天积累到 100 篇 Blog , Jekyll / Octopress 生成需要花费数分钟,而 Hexo 仍仅需几秒的血泪对比)。

为什么是在 VPS 上?

因为种种原因,如 GitHub 的 CDN 提供商是 fastly ,而 fastly 同时为 Twitter 提供服务; GitHub 上有许多并不和谐的项目…… GitHub 在国内多次被墙。使用 VPS 相比于 GitHub Pages 更为自由,比如可以为博客 配置 SSL ,无论是 从前面进还是从后面进 的你,最终目标都是把自己补全为一个 full-stack developer ,更早地向 Dev-Ops 方向迈进对你没有任何坏处。我始终认为,每一位 梦想成为魔法师的男孩 都应该有一个 VPS ,无论在你学习的哪个阶段,它总是可以用来做许多有趣抑或有用的事。

为什么通过 Git Hooks 更新?

其实你也可以本地什么都不做,每次都把文章的 *.md 通过 sftp 上传到 VPS 上,然后在 VPS 的网站目录中执行 hexo d -fg 更新博客,至于这个过程感受如何,请自行品味。

我还没有 VPS ?

/*以下提及的两款 VPS 同时均可搭建个人独享的代理服务*/

  • 如果可以接受每 5 的价格 -> DigitalOcean /*通过链接注册赠送 10 ,在开发者社区中有很好的声誉,是这个行业中为数不多的靠谱提供商之一,堪称最优性价比。*/
  • 如果可以接受每 4.5 的价格 -> 123Systems /*需要输入优惠码 LABOR 才可以以 4.5 的价格购买。机房一定要选择 Los Angeles 。如果缺货,属于正常情况,请耐心等待。 越便宜的东西,很可能就越容易出问题,因为商家不可能在卖给你那么便宜的同时还用足好料。而且如果出了问题,因为本身就是没有利润的东西,商家很可能也就不会当回事了。 作为练手可以,但是请一定做好数据备份,并在资金充裕时及时迁移到更好的服务。*/
  • 不愿意/不能为 VPS 付费?请使用 GitHub Pages 和免费代理服务,关于是免费代理服务,一句话忠告:免费的是最贵的。既然总还是需要购买代理服务的,为什么不自己搭建呢?

域名如何注册、配置?

如果还没有注册域名,推荐使用 namesilo ,这是目前在国外注册域名的最廉价注册商(各种数年一遇的活动不计),在这里注册的所有域名都 免费赠送域名保护 ,至于为什么不在国内注册?你懂的。使用优惠码 EscapeFromChina 可以获得 1 美元优惠(即每年 8.99 - 1 ≈ ¥50 ),可以使用 支付宝 付款。

配置即在域名注册商(如 namesilo )的管理台将域名的 NS 服务器设置为 DNSPod / CloudXNS 的,并在 DNSPod / CloudXNS 上为域名配置一条 A 记录和一条 @ 记录,记录值为你的 VPS IP ,配置较为基础,如果你不知道具体如何配置,请自行搜索。


搭建过程

搭建过程分为两部分,一部分在本机进行,另一部分则在服务端进行,大致需要完成的工作是在本机和 VPS 各安装一次 Hexo 和 Git ,并在 VPS 上安装 Nginx 服务器、配置 Git Hooks 以实现更新。

本机1

Linux Desktop

Linux Desktop 用户一定都是 Power User ,此处略。

Windows

以下下载 强烈 建议在 代理 下进行,或者自行寻找国内源。

安装 Node.js

Node.js 官网下载最新版,一路默认安装。

安装 Git

下载 Git for Windows ,一路默认安装。

Mac

安装 Homebrew

安装 Node.js

使用 Homebrew 安装 Node.js
brew install node

安装 Git

系统自带,无需手工安装。

Windows && Mac

/*以下操作, Windows 涉及 Node.js 、 Hexo 的操作都在 cmd 中输入 node 后执行,涉及 Git 的都在 Git Bash 中执行。 Mac 所有操作都在 Terminal 中执行。*/

创建网站目录

在任意位置创建一个文件夹,作为网站目录,并通过 cd 命令进入文件夹。

本地安装 Hexo

1
2
3
4
5
npm install -g hexo-cli
hexo init
npm install
hexo d -fg
hexo serve

打开 http://localhost:4000 即可看到你的站点(当然还没有发布到网络)。

本地 Git 配置

同样通过 cd 命令进入文件夹,之后执行

1
2
3
git init
git add .
git commit -m "Initial commit"

VPS

此处为 Debian / Ubuntu 在 root 用户下的操作,使用其他发行版本的一定是 Power User ,略。

关于连接 VPS , Windows 用户请使用 Putty (提示: Putty 中使用粘贴仅需鼠标右键), Mac 则直接在 Terminal 中输入 ssh root@IP

关于 vi 操作,不会用 vi 可以将下列命令中的 vi 全部替换为 nano (可能需执行 apt-get install nano -y ),如果觉得 nano 使用还是相对复杂,请使用 SFTP 工具, Windows 用户可使用 WinSCP , Mac 可使用 Cyberduck

1
2
3
4
5
6
7
8
9
apt-get update && apt-get upgrade -y
apt-get install git-core -y
curl -sL https://deb.nodesource.com/setup | bash
apt-get install nodejs -y
apt-get install nginx -y
cd /etc/nginx/sites-available
rm -rf default
touch example.com
vi example.com

在其中输入如下内容并保存,注意 IPexample 需替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen IP:80 ;
root /var/www/example.com/public;
server_name example.com;
access_log /var/log/nginx/example_access.log;
error_log /var/log/nginx/example_error.log;
location ~* ^.+\.(ico|gif|jpg|jpeg|png) {
root /var/www/example.com/public;
access_log off;
expires 1d;
}
location ~* ^.+\.(css|js|txt|xml|swf|wav) {
root /var/www/example.com/public;
access_log off;
expires 10m;
}
location / {
root /var/www/example.com/public;
if (-f request_filename) {
rewrite ^/(.*) /1 break;
}
}
}

之后执行

1
2
3
4
5
6
7
8
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
cd ~
mkdir repos && cd repos
mkdir example.com.git && cd example.com.git
git init --bare
cd hooks
touch post-receive
vi post-receive

在其中输入如下内容并保存,同样注意前四行的替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash -l
GIT_REPO=HOME/repos/example.com.git
TMP_GIT_CLONE=HOME/tmp/git/example.com
PUBLIC_WWW=/var/www/example.com
rm -rf {TMP_GIT_CLONE}
git clone GIT_REPO TMP_GIT_CLONE
rm -rf {PUBLIC_WWW}/*
cp -rf {TMP_GIT_CLONE}/* {PUBLIC_WWW}
cd ~
cd {PUBLIC_WWW}
hexo d -fg
cd ~
exit

最后执行

1
2
3
chmod +x post-receive
cd ~
service nginx restart

本机2

以下操作需要先 cd 到网站目录,注意替换

1
2
git remote add example root@IP:repos/example.com.git
git push example master

此时需要输入一次 VPS 密码,稍等片刻即安装完成。打开 example.com 即可看到你的博客。


关于更新 Blog

使用一款 MarkDown 编辑器写 Blog 。写完后将文件以 *.md 的格式保存在本地 [网站目录]\source\_posts 中。 文件编码必须为 UTF-8这一点仅 Windows 用户需注意。 如果你不知道使用什么 MarkDown 编辑器,请自行搜索。

每篇 Blog 都有固定的参数必须填写,参数如下,注意每个参数的 : 后都有一个空格。

title: title
date: yyyy-mm-dd
categories: category
tags: tag
#多标签请这样写:
#tags: [tag1,tag2,tag3]
#或者这样写:
#tags:
#- tag1
#- tag2
#- tag3
---
正文

cd 网站目录后执行

1
2
3
4
hexo d -fg
git add .
git commit -m "Post A New Blog"
git push example master

最后的几句话

靡不有初,鲜克有终。坚持做下去,你生活中的每一条河流都会在这里留下痕迹,你可以一直做到你老去的那一天,让你的整个人生沉淀成一个页面丰富的 html 文件夹

感谢@Sunyanzi 在加班中抽出时间对搭建的帮助:)

分享到 评论