这道题被我无视了……实际上没什么特别的逻辑。
思路就是记录分数,然后进行一个排序。然而,这道题容易坑掉的点在于,一旦不注意及时剪枝的话就会超时。
我具体剪枝和trick是这样的:
主要的剪枝是对分数的过滤。举个例子来说,2/3与4/6这两个分数是同一个。这里要用到的是欧几里得的辗转相除法,如果两个数字的最大公约数不为1的话,那么这个必定不是最优解,就可以不记录下来。
在这边我做了一个小trick,也就是在记录分数组合的时候,是从最小值写到最大值,这样就能在排序的时候减少交换的次数。
最后的排序操作,我使用的是冒泡,之所以用的它,是因为在记录数据的时候,大部分的数值其实已经排好了,需要交换的数量不会很多,这时候冒泡排序在排序完毕后立刻跳出的特性,可以让检查的次数比较少。(自我感觉)
以下附上代码:
#include <iostream>
#include <fstream>
using namespace std;
struct P
{
int a, b;
};
const int MAXN = 161;
P ps[MAXN*MAXN];
int n;
int N;
int getDivider(int n1, int n2)
{
if (n1%n2 == 0)
return n1 < n2 ? n1 : n2;
getDivider(n2, n1%n2);
}
void bubbleSort()
{
int i, j;
P tmp;
bool flag;
for (i = 1; i < n-1; i ++) {
flag = true;
for (j = 0; j < n-1-i; j ++) {
if (((float)ps[j+1].a)/ps[j+1].b < ((float)ps[j].a)/ps[j].b) {
tmp = ps[j];
ps[j] = ps[j+1];
ps[j+1] = tmp;
flag = false;
}
}
if (flag)
break;
}
}
int main()
{
ifstream fin ("frac1.in");
ofstream fout ("frac1.out");
int i, j;
fin >> N;
ps[n].a = 0;
ps[n].b = 1;
n ++;
for (i = N; i > 1; i --) {
ps[n].a = 1;
ps[n].b = i;
n ++;
}
for (i = 2; i < N; i ++) {
for (j = N; j > i; j --) {
if (getDivider(i, j) == 1) {
ps[n].a = i;
ps[n].b = j;
n ++;
}
}
}
ps[n].a = 1;
ps[n].b = 1;
n ++;
bubbleSort();
for (i = 0; i < n; i ++)
fout << ps[i].a << "/" << ps[i].b << endl;
return 0;
}
闲话时间,总觉得USACO的题目难度设置很不均匀,但也有可能是有我擅长不擅长的题目类型吧……