题目意思是给出2条直线,然后判断其是否相交,平行,还是重合。刚开始以为是判断2条线段的关系,用了黑书的模板写了,发现连样例都过不了。后面改了很多才过了。先判断2条直线所在的向量是否平行,这个可以判断这2个向量的叉积是否为0,然后再判断线段是否重合,可以选3点判断叉积是否为0。如果向量不平行的话,直接求交点。求交点的公式是用了黑书里面的方法,先求出2个叉积代表2个三角形的有向面积,然后根据定比分点的关系(面积的比例等于交点分其中一条线段的比例)可以推出计算公式。
有叉积和点积这2个工具确实能方便的解决很多问题。

代码如下:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <stdio.h>
#include <string.h>
#include <math.h>
struct Point
{
double fX;
double fY;
};
Point beg[2], end[2];
int nN;
const double fPrecision = 1e-8;

double Det(double fX1, double fY1, double fX2, double fY2)
{
return fX1 * fY2 - fX2 * fY1;
}

double Cross(Point a, Point b, Point c)
{
return Det(b.fX - a.fX, b.fY - a.fY, c.fX - a.fX, c.fY - a.fY);
}

int DblCmp(double fD)
{
if (fabs(fD) < fPrecision)
{
return 0;
}
else
{
return (fD > 0 ? 1 : -1);
}
}

double DotDet(double fX1, double fY1, double fX2, double fY2)
{
return fX1 * fX2 + fY1 * fY2;
}

double Dot(Point a, Point b, Point c)
{
return DotDet(b.fX - a.fX, b.fY - a.fY, c.fX - a.fX, c.fY - a.fY);
}

int BetweenCmp(Point a, Point b, Point c)
{
return DblCmp(Dot(a, b, c));
}

int SegCross(Point a, Point b, Point c, Point d, Point p)
{
double s1, s2, s3, s4;
int d1, d2, d3, d4;
d1 = DblCmp(s1 = Cross(a, b, c));
d2 = DblCmp(s2 = Cross(a, b, d));
d3 = DblCmp(s3 = Cross(c, d, a));
d4 = DblCmp(s4 = Cross(c, d, b));

Point e, f;
e.fX = a.fX - b.fX;
e.fY = a.fY - b.fY;
f.fX = c.fX - d.fX;
f.fY = c.fY - d.fY;
if (DblCmp(Det(e.fX, e.fY, f.fX, f.fY)) == 0)//2个向量共线
{
if (d1 * d2 > 0 d3 * d4 > 0)//不在同一条直线上
{
return 0;
}
else
{
return 2;
}
}

//2条直线相交
p.fX = (c.fX * s2 - d.fX * s1) / (s2 - s1);
p.fY = (c.fY * s2 - d.fY * s1) / (s2 - s1);
return 1;
}

int main()
{
//freopen("out.txt", "w", stdout);
while (scanf("%d", &nN) == 1)
{
printf("INTERSECTING LINES OUTPUT\n");
Point p;
for (int i = 0; i < nN; ++i)
{
scanf("%lf%lf%lf%lf", &beg[0].fX, &beg[0].fY, &end[0].fX, &end[0].fY);
scanf("%lf%lf%lf%lf", &beg[1].fX, &beg[1].fY, &end[1].fX, &end[1].fY);
int nRet = SegCross(beg[0], end[0], beg[1], end[1], p);
if (nRet == 0)
{
printf("NONE\n");
}
else if (nRet == 1)
{
printf("POINT %.2f %.2f\n", p.fX, p.fY);
}
else
{
printf("LINE\n");
}
}
printf("END OF OUTPUT\n");
}

return 0;
}

这是一个计算几何的题目。题意是,按顺序给一系列的线段,问最终哪些线段处在顶端。只需要穷举判断,当前的线段会与哪些线段有交点即可。也就是暴力求解,但是线段数目N有10的5次方,平方算法是不能过的。这个题能过的原因是题目描述里面说了,top的stick不会超过1000个。那么修改下暴力的方式题目就能过了。
从小到大枚举每个棍子,判断它是否与后面的棍子相交,如果相交直接把当前棍子的top属性置为false,然后break内层循环。这样就不会超时了,暴力也是需要技巧的,这句话说的很对啊。
判断2条线段是否相交的算法直接按照黑书上的模板代码写了,那个模板代码还不错吧。。。

代码如下:

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
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdio.h>
#include <string.h>
#include <math.h>
#define MAX_N (100000 + 10)
struct POS
{
double fX;
double fY;
};

POS begs[MAX_N], ends[MAX_N];
bool bAns[MAX_N];
int nN;
const double fPrecision = 1e-8;

double Det(double fX1, double fY1, double fX2, double fY2)
{
return fX1 * fY2 - fX2 * fY1;
}

//以a作为公共点,计算叉积
double Cross(POS a, POS b, POS c)
{
return Det(b.fX - a.fX, b.fY - a.fY, c.fX - a.fX, c.fY - a.fY);
}

int DblCmp(double fD)
{
if (fabs(fD) < fPrecision)
{
return 0;
}
else
{
return fD > 0 ? 1 : -1;
}
}
//
bool IsSegCross(int nI, int nJ)
{
return (DblCmp(Cross(begs[nI], ends[nI], begs[nJ]))
^ DblCmp(Cross(begs[nI], ends[nI], ends[nJ]))) == -2
(DblCmp(Cross(begs[nJ], ends[nJ], begs[nI]))
^ DblCmp(Cross(begs[nJ], ends[nJ], ends[nI]))) == -2;
}

int main()
{
while (scanf("%d", &nN), nN)
{
for (int i = 1; i <= nN; ++i)
{
scanf("%lf%lf%lf%lf", &begs[i].fX, &begs[i].fY,
&ends[i].fX, &ends[i].fY);
}

memset(bAns, true, sizeof(bAns));

//暴力也是需要技巧的
for (int i = 1; i < nN; ++i)
{
for (int j = i + 1; j <= nN; ++j)
{
if (IsSegCross(i, j))
{
bAns[i] = false;
break;
}
}
}

printf("Top sticks:");
bool bPre = false;
for (int i = 1; i <= nN; ++i)
{
if (bAns[i])
{
if (bPre)
{
printf(",");
}
bPre = true;
printf(" %d", i);
}
}
printf(".\n");
}

return 0;
}

这个题不错,居然需要在dfs里面写bfs。题意类似于图像识别里面,搜索一张图像里面的某个指定区域里面有几个斑点,题意里面的斑点是指色子。


30 15
…………………………
…………………………
………………………..
**
……*…………
X*
…..X*………..
*….*X…………
X…..**………….
*…….…………..
…………………………
……..**
……..**…..
…….X**…..X**X…..
……*……**…..
…..**X…….X**X…..
……..……..*…..
…………………………
比如上面这个30 15的图片里面,一共有四个区域,作为区域的底色,然后是求区域里面有多少个X的块。这个题单纯dfs的话,很没办法,因为无法一次性把连接在一起的X都搜索了。比如,
5 5

XXX*X XXX*X ..... X***X XX*** 的时候,dfs很明显就会出现问题,因为会先离开X块,再次回到X块,计数就会出现问题了。因此只能遇到X的时候,进行一次bfs,将与其相连接的X全部搜索掉。。。并且找到与当前X块相连接的一个*的位置,如果有这样的位置,就继续进行dfs。

</div>
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;

int nW, nH;
char szData[100][100];
bool bVisit[100][100];
int nNum;
int nDice[100];
int nAdd[4][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};

bool IsPosOk(int i, int j)
{
return i >= 0 i < nH j >= 0 j < nW;
}

struct POS
{
int nI;
int nJ;
};

bool Bfs(int nI, int nJ)
{
bool bRet = false;
queue<POS> qp;
POS pos = {nI, nJ};
int i = nI, j = nJ;

qp.push(pos);
while (qp.empty() == false)
{
POS head = qp.front();
qp.pop();

for (int m = 0; m < 4; ++m)
{
int nNextI = head.nI + nAdd[m][0];
int nNextJ = head.nJ + nAdd[m][1];

if (IsPosOk(nNextI, nNextJ) bVisit[nNextI][nNextJ] == false)
{
if (szData[nNextI][nNextJ] == 'X')
{
bVisit[nNextI][nNextJ] = true;
POS pos = {nNextI, nNextJ};
qp.push(pos);
}
else if (szData[nNextI][nNextJ] == '*')
{
bRet = true;
nI = nNextI;// 这里是返回新的dfs位置
nJ = nNextJ;
}
}
}
}

return bRet;
}

void dfs(int i, int j, int nNum)
{
bVisit[i][j] = true;
if (szData[i][j] == 'X')
{
nDice[nNum]++;
bool bDfs = Bfs(i, j);//扩散掉当前连通的所有'X'
if (bDfs == false)
{
return;
}
else
{
dfs(i, j, nNum);
}
}

for (int m = 0; m < 4; ++m)
{
int nNextI = i + nAdd[m][0];
int nNextJ = j + nAdd[m][1];

if (IsPosOk(nNextI, nNextJ) bVisit[nNextI][nNextJ] == false
szData[nNextI][nNextJ] != '.')
{
dfs(nNextI, nNextJ, nNum);
}
}
}

int main()
{
int nCases = 1;

while (scanf("%d%d", &nW, &nH), nW + nH)
{
for (int i = 0; i < nH; ++i)
{
scanf("%s", szData[i]);
}
memset(bVisit, false, sizeof(bVisit));
memset(nDice, 0, sizeof(nDice));
nNum = 0;

for (int i = 0; i < nH; ++i)
{
for (int j = 0; j < nW; ++j)
{
if (szData[i][j] == 'X' bVisit[i][j] == false)
{
dfs(i, j, nNum);
nNum++;
}
}
}
sort(nDice, nDice + nNum);

printf("Throw %d\n", nCases++);
for (int i = 0; i < nNum; ++i)
{
printf("%d%s", nDice[i], i == nNum - 1 ? "\n" : " ");
}
printf("\n");
}

return 0;
}

这是一个貌似很麻烦的题,题目要求是将一颗用ascii码绘画出来的树,转换为其一种字符串表示,这种字符串表示好像是叫做什么广义表什么的。
比如,
A

|


B C D

| |


E F G 对应的字符串表示 **(A(B()C(E()F())D(G())))**

</div>
比较纠结的是如何读取数据,如何递归,如果建立树的话,也麻烦,因为还是颗不定叉的树。最主要的是如何方便地递归。最后知道了一个比较巧妙的方法,先一次性把一组数据读入字符串数组里面,再在这个字符串数组上进行递归处理。这样的话,就能很方便的找到树里面节点
的关系了。
而一次读一个字符就想进行递归是没办法确定节点的关系的,不递归估计更很难写,完全没头绪。。。

代码如下:

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
#include <stdio.h>
#include <stdlib.h>

char szLines[210][210];
int nNumOfLine;

void GetAns(int i, int j)
{
//printf("i:%d, j:%d, %c\n", i, j, szLines[i][j]);

if (szLines[i][j] != '\0')
{
putchar(szLines[i][j]);
//printf("%c", szLines[i + 1][j]);
if (szLines[i + 1][j] == '|')
{
int nBeg, nEnd;
nBeg = nEnd = j;
while (nBeg >= 0 szLines[i + 2][nBeg] == '-')
{
--nBeg;
}
while (szLines[i + 2][nEnd] == '-')
{
++nEnd;
}
//printf("nBeg:%d, nEnd:%d\n", nBeg, nEnd);
putchar('(');
for (int k = nBeg; k {
if (szLines[i + 3][k] != ' ' szLines[i + 3][k] != '\0')
{
GetAns(i + 3, k);
}
}
putchar(')');
}
else
{
printf("()");
}
}

}

int main()
{
int nN;
char ch;

scanf("%d", &nN);
getchar();
while (nN--)
{
nNumOfLine = 0;
memset(szLines, 0, sizeof(szLines));
while (gets(szLines[nNumOfLine]), szLines[nNumOfLine][0] != '#')
{
//printf("%s\n", szLines[nNumOfLine]);
nNumOfLine++;
}
if (nNumOfLine == 0)
{
printf("()\n");
continue;
}
int i, j;
i = 0;
for (j = 0; szLines[0][j] == ' '; ++j);
//printf("i:%d, j:%d\n", i, j);
putchar('(');
GetAns(i, j);
putchar(')');
putchar('\n');
}

return 0;
}

这个题目的意思是要计算一些c语言表达式的值。这些表达式有+-还有++,—操作符与a-z这些变量组合而成。a-z的权值是1-26。比如,表达式 c+f—+—a,得出值是9,其它变量的值也需要计算出来。
这个题目感觉比较麻烦,刚开始一点思路也没有,还写了个错误的方法,浪费了时间。后面我的思路是 (+,-) (—,++)(变量)(—,++),这个匹配式子的意思是先处理二元操作符,然后处理前置,再处理变量,再处理后置,如果发现没有后置操作符,则把读取的数据重新写回数据流里面,下次再处理。

代码如下:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <stdio.h>
#include <string.h>
#include <sstream>
#include <algorithm>
using namespace std;
struct INFO
{
char ch;
int nValue;
char chAdd;
bool operator < (const INFO info) const
{
return ch < info.ch;
}
};

INFO infos[200];
char szLine[200];

bool GetNextChar(stringstream ss, char ch)
{
while (ss >> ch)
{
if (ch != ' ');
{
return true;
}
}
return false;
}

int main()
{
while (gets(szLine))
{
printf("Expression: %s\n", szLine);
memset(infos, 0, sizeof(infos));
stringstream ss(szLine);
char ch;
int nNum = 0;
int nValue = 0;
char chOper;
bool bOk = true;
bool bFirst = true;
while (1)
{
if (bFirst)
{
chOper = '+';
bFirst = false;
}
else
{
bOk = GetNextChar(ss, ch);
if (!bOk) break;
chOper = ch;
}

bOk = GetNextChar(ss, ch);
if (!bOk) break;

if (ch == '-')//前置--
{
bOk = GetNextChar(ss, ch);
if (!bOk) break;//-
bOk = GetNextChar(ss, ch);
if (!bOk) break;//读取字母

infos[nNum].ch = ch;
infos[nNum].nValue = ch - 'a';

if (chOper == '+')
{
nValue += infos[nNum].nValue;
}
else
{
nValue -= infos[nNum].nValue;
}
++nNum;
}
else if (ch == '+')//前置++
{
bOk = GetNextChar(ss, ch);
if (!bOk) break;//+
bOk = GetNextChar(ss, ch);
if (!bOk) break;//读取字母

infos[nNum].ch = ch;
infos[nNum].nValue = ch - 'a' + 2;

if (chOper == '+')
{
nValue += infos[nNum].nValue;
}
else
{
nValue -= infos[nNum].nValue;
}
++nNum;
}
else
{
infos[nNum].ch = ch;
infos[nNum].nValue = ch - 'a' + 1;

if (chOper == '+')
{
nValue += infos[nNum].nValue;
}
else
{
nValue -= infos[nNum].nValue;
}
//读取后置操作符
char chOne;
char chTwo;
bOk = GetNextChar(ss, chOne);
if (!bOk)
{
++nNum;
break;
}
bOk = GetNextChar(ss, chTwo);
if (!bOk)
{
++nNum;
break;
}

if (chOne == chTwo)
{
if (chOne == '+')
{
infos[nNum].chAdd = '+';
}
else
{
infos[nNum].chAdd = '-';
}
}
else
{
ss.putback(chTwo);
ss.putback(chOne);
}
++nNum;
}
}

printf(" value = %d\n", nValue);
sort(infos, infos + nNum);
for (int i = 0; i < nNum; ++i)
{
if (infos[i].chAdd == '+')
{
infos[i].nValue++;
}
else if (infos[i].chAdd == '-')
{
infos[i].nValue--;
}
printf(" %c = %d\n", infos[i].ch, infos[i].nValue);
}
}

return 0;
}

题意是用字符串描述的一棵四叉树,读取字符串获得最终叶子节点的颜色。输入是2个字符串,根据这2个字符串建立2个四叉树。然后对于,指定位置的叶子节点,如果2颗树的叶子颜色其中一个为黑色,那么ans++,输出的就是ans。
类似树形结构的东西,直接一个函数递归求解即可。函数参数,一般是字符串地址,当前位置,然后还有其它在递归时候需要用到的东西。

代码如下:

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
#include <stdio.h>
#define BLACK (1)
#define WHITE (2)
#define MAX (32)
int nStateA[MAX][MAX];
int nStateB[MAX][MAX];

char szOne[10000];
char szTwo[10000];

void GetState(int nState[MAX][MAX], char* pszLine, int nPos,
int nSize, int nX, int nY)
{
if (pszLine[nPos] == 'p')
{
++nPos;
GetState(nState, pszLine, nPos, nSize / 2, nX + nSize / 2, nY);
GetState(nState, pszLine, nPos, nSize / 2, nX, nY);
GetState(nState, pszLine, nPos, nSize / 2, nX, nY + nSize / 2);
GetState(nState, pszLine, nPos, nSize / 2, nX + nSize / 2, nY + nSize / 2);
}
else
{
for (int i = nX; i < nX + nSize; ++i)
{
for (int j = nY; j < nY + nSize; ++j)
{
if (pszLine[nPos] == 'e')
{

nState[i][j] = WHITE;
}
else
{
nState[i][j] = BLACK;
}
}
} ++nPos;
}
}

int main()
{
int nCases;

scanf("%d\n", &nCases);
while (nCases--)
{
gets(szOne);
gets(szTwo);
int nPos = 0;
GetState(nStateA, szOne, nPos, MAX, 0, 0);
nPos = 0;
GetState(nStateB, szTwo, nPos, MAX, 0, 0);
int nAns = 0;
for (int i = 0; i < MAX; ++i)
{
for (int j = 0; j < MAX; ++j)
{
if (nStateA[i][j] == BLACK || nStateB[i][j] == BLACK)
{
nAns++;
}
}
}
printf("There are %d black pixels.\n", nAns);
}

return 0;
}

这也是一个数学题,刚开始还真以为好难的样子,需要用到什么数论的理论啊。其实,只要去找规律就行了。
题意是给出一个进制,一个数字的最低位,和另外的一个数字,比如10进制,第一个数字的最低位是7,第二个数字是4,根据这些信息,和规则(XXXXX7 4 = 7XXXXX,例子: 179487 4 = 717948 )求出第一个数字的最小长度。这个规则的意思是乘积是把第一个数字的最低位移动到最高位上去就行了。
貌似很难的样子,其实用笔在纸上求一下XXXXX7 4 = 7XXXXX就会发现。XXXXX7的每一位都是能够确定的,当然顺序是从最低位到最高位开始。因为知道最低位,所以次低位一定是最低位第二个数%base。以此类推,递归下去即可。最终条件是,没有进位了,而且乘积+原来的进位==最低位。
我用的递归完全可以改成循环的形式,这样速度应该会快些。

代码如下:

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

#include <stdio.h>

int nBase;
int nTwo;
int nOneLow;

int GetMin(int nLow, int nCarry, int nNum)
{
//printf("nLow:%d, nCarry:%d, nNum:%d\n", nLow, nCarry, nNum);
int nTemp = nCarry + nLow * nTwo;
if (nTemp == nOneLow)
{
return nNum;
}

return GetMin(nTemp % nBase, nTemp / nBase, nNum + 1);
}

int main()
{
//freopen("out.txt", "w", stdout);
while (scanf("%d%d%d", &nBase, &nOneLow, &nTwo) == 3)
{
printf("%d\n", GetMin(nOneLow, 0, 0) + 1);
}

return 0;
}

这是一个很神的数学题吧。基本上过这个题的很多都会wa10多次,而且这个题好像简单的枚举其中的一个指数值都能过,可能是数据量比较小。
但是,这个题还是有数学的解法的。但是,即使找到了这个正确的解法,过题的话,也是一件很困难的事情。题意大致如下:一只猫,高度为H,戴了一个帽子,帽子里面有N只猫(N是常数,且未知),同样帽子里面的猫也戴了帽子,但是这些猫的高度变成了H / (N + 1),会向下取整。以此递归下去,直到最后的猫的高度都为1为止。现在,给出H和高度为1的猫的数量。要求的是高度大于1的猫的数量,以及所有猫的高度之和。
很别扭吧。通过上面的信息,得出2个式子。假设one代表为高度为1的猫的数量。one = N的n次。H >= (N + 1)的n次。注意第二个式子不一定取等号,因为很多时候都是不能整除的。现在要求N和n。2个方程解2个未知数,应该能解出来。但是,注意的是其中一个还是不等式。。。
指数关系很多时候会转换为对数的关系。所以,继续求对数,有lgH >= n lg(N + 1)。其中,由第一个式子可以得到n = lg(one) / lg(N)。那么最终转换为:lgH >= (lg(one) / lgN) lg(N + 1)。换个形式就是lgH / lg(One) >= lg(N + 1) / lgN。现在,已经很清晰了。因为,函数lg(N + 1) / lg(N) 是单调递减的。看到单调的函数,马上就会知道可以二分了。意思是,我们可以二分出一个N让lg(N + 1) / lgN 最接近lgH/lg(One),而且是小于lgH / lg(One)的。剩下的工作就只是求和而已了。
写二分的时候,有一个地方可以注意一下。因为 lg(N + 1) / lgN 可能会出现除数为0的情况,所以可以进一步转换为lgH lgN >=lg(N + 1) lg(one)。 也是求一个N让上面那个不等式2边的值最接近,而且右边小于左边。能很快写对这个题真不是件容易的事情。。。
代码如下:

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
#include <stdio.h>
#include <math.h>

int main()
{
int nInitH, nOnes;
int nN, n;

while (scanf("%d%d", &nInitH, &nOnes), nInitH + nOnes)
{
int nBeg = 1;
int nEnd = nOnes;
int nMid;

while (nBeg <= nEnd)
{
nMid = (nBeg + nEnd) / 2;

double fRes = log10(nInitH) * log10(nMid);
double fTemp = log10(nMid + 1) * log10(nOnes);
if (fabs(fRes - fTemp) < 1e-10)
{
//printf("Find nN:%d\n", nMid);
nN = nMid;
break;
}
else if (fTemp > fRes)
{
nBeg = nMid + 1;
}
else
{
nEnd = nMid - 1;
}
}

n = floor(log10(nInitH) / log10(nN + 1) + 1e-9);
//printf("nN:%d, n:%d\n", nN, n);

int nSum = 0;
int nLazy = 0;
int nNum = 1;
for (int i = 0; i <= n; ++i)
{
nSum += nNum * nInitH;
nLazy += nNum;
nNum *= nN;
nInitH /= (nN + 1);
}

printf("%d %d\n", nLazy - nOnes, nSum);
}

return 0;
}

这是一个几何题。题意是给出一系列点,点最多才15个,求一个这里面的三个点组合出来的三角形,其面积是最大的,而且没有任何其它的点在这个三角形的内部和边界上。求三角形的面积,题目上面已经给了公式,也可以用0.5|a||b|sin(a,b)求,这里的a和b指的是2条边代表的向量。
现在就剩下一个问题了,怎么判断一个点在三角形的内部和边界上。在边界上,比较好判断,判断是否共线,然后再点是在线段的内部。
具体说明下,判断一个点在三角形内部的思路。我用的还是线性规划的思想。*如果该点在三角形的内部,那么任取三角形的一条边,该内部点和剩余的三角形的一个顶点必定在三角形的那条的边的同一侧。
这个方法也可以推广到N边的凸多边形,证明的话很简单,因为线性规划一直在划分区域。所以,划分到最后围起来的区域就是凸多边形的内部了。
至于写代码的话,由于是第一次写这种几何题,写得很凌乱。

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <stdio.h>
#include <math.h>

#define MAX (20)
int nN;
struct Point
{
char szLabel[5];
int x;
int y;
};
Point points[MAX];

//三点是否共线
bool IsOneLine(int nOne, int nTwo, int nThree)
{
int nA = points[nTwo].x - points[nOne].x;
int nB = points[nTwo].y - points[nOne].y;
int nC = points[nThree].x - points[nOne].x;
int nD = points[nThree].y - points[nOne].y;

return (nA * nD == nB * nC);
}

//点nOne和点nTwo是否在直线(nBeg,nEnd)的同一侧(不能在直线上)
bool IsSameSide(int nBeg, int nEnd, int nOne, int nTwo)
{
//求直线的向量
int nA = points[nBeg].x - points[nEnd].x;
int nB = points[nBeg].y - points[nEnd].y;

//直线方程为nB(x - points[nBeg].x) - nA(y - points[nBeg].y) = 0
//分别用nOne和nTwo的坐标代入直线方程计算结果,然后将结果相乘
//乘积必须大于0
int nRes = (nB * (points[nOne].x - points[nBeg].x) - nA * (points[nOne].y - points[nBeg].y))
* (nB * (points[nTwo].x - points[nBeg].x) - nA * (points[nTwo].y - points[nBeg].y));

if (nRes > 0)
{
//printf("点:%d,点:%d,在直线nBeg:%d, nEnd:%d的同一侧\n", nOne, nTwo, nBeg, nEnd);
}
return nRes > 0;
}

//点是否在三角形(nOne, nTwo, nThree)外部
bool PointOutTriangle(int nOne, int nTwo, int nThree, int nPoint)
{
//前面3个ifelse是判断点是否在边上
if (IsOneLine(nOne, nTwo, nPoint))
{
if ((points[nOne].x - points[nPoint].x) * (points[nTwo].x - points[nPoint].x) <= 0)
{
return false;
}
}
else if (IsOneLine(nOne, nThree, nPoint))
{
if ((points[nOne].x - points[nPoint].x) * (points[nThree].x - points[nPoint].x) <= 0)
{
return false;
}
}
else if (IsOneLine(nTwo, nThree, nPoint))
{
if ((points[nTwo].x - points[nPoint].x) * (points[nThree].x - points[nPoint].x) <= 0)
{
return false;
}
}

//下面的IsSameSide如果nPoint在直线的(nOne,nTwo)的外侧也会判断为假
//所以需要先在上面判断点是否在边的内侧
return !(IsSameSide(nOne, nTwo, nThree, nPoint)
IsSameSide(nOne, nThree, nTwo, nPoint)
IsSameSide(nTwo, nThree, nOne, nPoint));
}

bool IsValid(int nOne, int nTwo, int nThree)
{
if (IsOneLine(nOne, nTwo, nThree))
{
//printf("点:%d,%d,%d共线\n", nOne, nTwo, nThree);
return false;
}

for (int i = 0; i < nN; ++i)
{
if (i == nOne || i == nTwo || i == nThree)
{
continue;
}

if (!PointOutTriangle(nOne, nTwo, nThree, i))
{
//printf("点:%d, 在三角形:%d,%d,%d内部\n", i, nOne, nTwo, nThree);
return false;
}
}

return true;
}

//计算三角形(nOne, nTwo, nThree)的面积
double GetArea(int nOne, int nTwo, int nThree)
{
return 0.5 * fabs((points[nThree].y - points[nOne].y) * (points[nTwo].x - points[nOne].x)
- (points[nTwo].y - points[nOne].y) * (points[nThree].x - points[nOne].x));
}

int main()
{
while (scanf("%d", &nN), nN)
{
for (int i = 0; i < nN; ++i)
{
scanf("%s%d%d", points[i].szLabel, &points[i].x, &points[i].y);
}

double fMaxArea = 0.0;
int nI = -1, nJ = -1, nK = -1;
for (int i = 0; i < nN - 2; ++i)
{
for (int j = i + 1; j < nN - 1; ++j)
{
for (int k = j + 1; k < nN; ++k)
{
if (IsValid(i, j, k))
{
//printf("i:%d,j:%d,k:%d valid\n", i, j, k);
double fArea = GetArea(i, j, k);
//printf("Area:%f\n", fArea);
if (fArea > fMaxArea)
{
nI = i;
nJ = j;
nK = k;
fMaxArea = fArea;
}
}
}
}
}
printf("%s%s%s\n", points[nI].szLabel, points[nJ].szLabel, points[nK].szLabel);
}

return 0;
}

这也算一个数学类的杂题吧。题目本身比较有意思,解题的思路很需要猜想。题目的意思用+和-去替代式子(? 1 ? 2 ? … ? n = k)中的?号,对于指定的K,求最小的n。For example: to obtain k = 12 , - 1 + 2 + 3 + 4 + 5 + 6 - 7 = 12 with n = 7。
这个题,我的思路大致如下。首先,K可能是正的也是负的,而且显然负的情况,有相应的正对应情况。那么考虑所有k为正的情况。由于k一定小于等于n(n+1)/2的,所以可以先求出这样的最小n。这个可以二分搜索,或者直接用解不等式方程(不过这种方法一直wa了)。
然后就剩下的是第二点了,假设a + b = n
(n+1)/2, a - b = k。可以得到 n(n+1)/2 - k = 2 b。意思是,必须满足 n(n+1)/2和k的差为偶数。假如满足了,这样的n是不是一定OK了???
答案是肯定的,这一点就是需要猜想的地方了。因为,仔细观察下,1到n的数字可以组合出任意的1到 n
(n+1)/4之间的数字,这个数字即是b。至于证明,可以用数学归纳法从n==1开始证明了。。。至此已经很简单了。
由于求n存在2种不同的方法,而且我开始用解一元二次不等式的方法求的N,出现了浮点误差,一直WA了。后面改成二分才过了。。。

代码如下:

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
#include <stdio.h> 
#include <math.h>

int GetN(int nK)
{
int nBeg = 1;
int nEnd = sqrt(nK * 2) + 2;

while (nBeg <= nEnd)
{
int nMid = (nBeg + nEnd) / 2;
int nTemp = (nMid * nMid + nMid) / 2;
if (nTemp >= nK)
{
nEnd = nMid - 1;
}
else
{
nBeg = nMid + 1;
}
}

return nEnd + 1;
}

int main()
{
int nK;
int nTest;

scanf("%d", &nTest);
while (nTest--)
{
scanf("%d", &nK);
if (nK < 0)
{
nK *= -1;
}
//int nN = ceil(sqrt(2 * fabs(1.0 * nK) + 0.25) - 0.5 + 1e-9);
//上面那种方法存在浮点误差,wa了三次
int nN = GetN(nK);

while (1)
{
if (((nN * nN + nN) / 2 - nK) % 2 == 0)
{
printf("%d\n", nN);
break;
}
++nN;
}
if (nTest)
{
printf("\n");
}
}

return 0;
}