EDAthon是一个围绕硬件相关的一个比赛,涉及的面居多,每个题都是基于某个领域的经典问题,今年的题目涉及具体IP核实现,PCB布线算法,IP核布局算法,指令调度相关,神经网络的量化等
Floorplanning问题大概如下:给你若干个矩形(模块),让你不重叠的放置在平面中使得最终的包围矩形最小,当然可以加些约束,比如指定某些模块靠的近一些,或者某些模块是软的(只要保证面积不变,长宽比可以任意),或者指定模块四周要留某个单位的空余,等等
如下图所示,10个模块的最小包围矩形面积为100*100
题目给你n个矩形,边长为浮点数,和m对约束,约束为两个矩形尽可能的离得近,不能旋转矩形。最终输出每个矩形最终状态的左下角坐标。
PS:题目未说明n的大小,但上限不会太大,可以按照200作为上限。
输入样例如下:
8
400.0 300.0
400.0 140.0
330.0 240.0
280.0 220.0
150.0 240.0
380.0 130.0
530.0 210.0
230.0 220.0
1
1 2
输出样例如下:
0.0 0.0
400.0 0.0
0.0 300.0
400.0 140.0
680.0 140.0
330.0 380.0
0.0 540.0
530.0 510.0
此样例最终状态如下图所示:
评判标准:
F c o s t = ( A f + D ) / A c Fcost = ( Af+ D ) / Ac Fcost=(Af+D)/Ac
其中 F c o s t Fcost Fcost为最终代价,使其最小化, A f Af Af是最终的包围矩形面积, D D D是m对矩形间曼哈顿距离的平方和的和, A c Ac Ac是所有矩形的面积和
时空约束:
R u n t i m e : 5 m i n Runtime:5 ~min Runtime:5 min
M e m o r y : 1 G B Memory:1~GB Memory:1 GB
主要参考了这篇论文,可能需要梯子,也可以参考下面自行下载
BSG(Bounded slicing grid)- structure:Nakatake S, Fujiyoshi K, Murata H, et al. Module placement on BSG-structure and IC layout applications[C]//Proceedings of International Conference on Computer Aided Design. IEEE, 1996: 484-491
该论文主要提出了BSG结构,可以很巧妙的构建出模块的分布,进而可以计算每个分布的包围矩形,再结合模拟退火随机算法,最终得到的解还挺理想。
简单介绍下论文提出的架构。
如上右图1B是一个4*3的BSG图,首先观察该图线段很有规律,比如横向观察,第一层线段长度为2,1第二层为1,2,再往上就循环如此,纵向类似。当然这个图是可以无限放大的。每个交错的横纵线段中有很多ROOM,如图1A所示,这里的ROOM可以放一个矩形(模块)
在上述的BGS图中,横向和纵向分别建图,如上图2所示,可以很巧妙的发现每个图的节点位置都是线段长度为2的中心点。我们有可以很巧妙的发现在Gh和Gv中每个room都会有一条边覆盖,进而利于我们在可以在任意的room中放置矩形(模块)。每个图都要设置超级源点和超级汇点。
现在进入实战环节,假如有4个矩形 a , b , c , d a,b,c,d a,b,c,d,长宽如上图A所示,假如我们的BSG图为4*4,恰好每个模块所在的位置如上图B所示,那么我们就可以得到上图CD的Gh,Gv,如果在BSG图某个room有模块时,那么在Gh图对应边的权值应为对应模块的宽,同理Gv对应的权值应为模块的高,如果BSG图某个room没有模块,对应的Gh,Gv边权都为0。
通过Gh,Gv我们就可以得到每个模块在最终布局的具体坐标
比如 a a a模块,首先根据Gh图对应 r o o m room room源点 V 1 V1 V1(边权为8的左下角的点),从超级源点 S v Sv Sv到 V 1 V1 V1的最长路径为0,即表示 a a a模块最终位置的 x x x坐标为0。同理可以得到 y y y坐标为9,进而得到 a a a模块的左下角坐标,其他模块类似。
重点是我们可以直接通过Gh,Gv求得最终包围矩形的宽和长
只需要分别在Gh,Gv图中跑一边最长路径即可。利用记忆化搜索可以把时间降低到O(N),N为节点的个数。
该样例最终的布置如下:
基于论文中所述的BSG结构,可以轻松的建图,8*8的图,Gh,Gv原始图如下,可能会更详细点。代码就是基于此图构建的
首先模拟退火算法的原理具体不多介绍。
主要利用 Simulated Annealing 来确定每个模块在BSG的具体位置,以及再退火的过程中如何变化每个模块的位置。
首先是初始化,直接暴力“塞进去”,按照编号一个个进入ROOM编号。
退火过程就是随机挑选两个位置,如果两个位置都为空,再重新挑选,直到找到至少有一个位置不为空为止,然后就是交换两个ROOM的值,更新图和相关的变量,然后就是退火的老一套,如果当前Cost是上次的Cost小,则直接交换,否则随机一波值,让它有一定概率Cost变大。然后就让它随机下去,直到温度小于阈值。
代码很水,有手就能敲,仅供参考,可能有的函数懒得加注释了,反正C++都能看懂
#include
using namespace std;
#define MX 100
#define pii pair
#define fi first
#define se second
vector<pii> input; // 输入的模型,暂定为矩形
vector<pii> Vh,Vv; // 顶点 对应 坐标
vector<vector<int>> graphH,graphV; // 节点的孩子节点
map<pii,int> mGh, mGv; // 坐标对应顶点
int Gh[MX][MX]; // Gh 节点权重
int Gv[MX][MX]; // Gv 节点权重
int dpH[MX*MX]; // 求Gh最大路径记忆化数组
int dpV[MX*MX]; // 求Gv最大路径记忆化数组
int modulesNum; // 模型总个数
pii table[MX*MX]; // 随机用到的表格, fi : index, se : 摆放姿势,只考虑横竖
int vertex[MX*MX]; // input point
pii shape; // 预定平面大小
int vNum; // 建图之后的顶点个数,G_0 为源点,G_vNum 为汇点
int changeSum; // 总随机的次数
int sumArea; // 模块总面积
vector<pii> close; // 某些模块靠近
int closeNum; // 靠近模块对的个数
clock_t startClock, endClock; // 记时tag
double coe[10] = {0.9993, 0.9994, 0.9995, 0.9996, 0.9793, 0.9696, 0.9981, 0.9877, 0.9699, 0.9991};
int finalHeight, finalwidth; // 最终的长宽
pii finalTable[MX*MX]; // 最终的调度结果
int finalGh[MX][MX]; // 最终的 Gh 节点权重
int finalGv[MX][MX]; // 最终的 Gv 节点权重
int finalVertex[MX*MX];
double finalCost;
void printTable();
void initPlaneSize();
void initGraphVertex();
bool isOdd(int x);
int dfsH(int u);
int dfsV(int u);
pii index2Coordinate(int idx);
pii getCoordinate(int idx, int p);
void SA();
string int2string(int x) {
stringstream ss;
ss << x;
string res;
ss >> res;
return res;
}
char mp[200][200];
void draw(int x1, int y1, int x2, int y2) {
if (x1 == x2) {
for (int j = y1; j < y2; j++) {
mp[x1][j] = '_';
}
} else for (int i = x1; i < x2; i++) {
mp[i][y1] = '|';
}
}
void printMP() {
for (int i = 0; i < dpV[0] + 4; i++) cout << "_";cout <<endl;
for (int i = dpH[0]; i >= 0; i--) {
cout << "| ";
for (int j = 0; j <= dpV[0]; j++) {
printf("%c", mp[i][j]);
}cout << " |\n";
}
cout << "|";for (int i = 0; i < dpV[0] + 3; i++) cout << "_";cout << "|";cout <<endl;
}
void printGhGvScheduing(pii _table[], int _Gh[][MX], int _Gv[][MX]) {
puts("Table");
for (int i = shape.fi - 2; i >= 0; --i) {
for (int j = 0; j < shape.se - 1; ++j) {
if (_table[i * (shape.fi - 1) + j].fi == -1) printf("None ");
else printf("%d_%d ", _table[i * (shape.fi - 1) + j].fi, _table[i * (shape.fi - 1) + j].se);
} puts("");
}
puts("Gh");
for (int i = shape.fi - 2; i >= 0; --i) {
for (int j = 0; j < shape.se - 1; ++j) {
int now = i * (shape.fi - 1) + j;
pii tmp = getCoordinate(now, 0);
printf("%2d ", _Gh[tmp.fi][tmp.se]);
} puts("");
}
puts("Gv");
for (int i = shape.fi - 2; i >= 0; --i) {
for (int j = 0; j < shape.se - 1; ++j) {
int now = i * (shape.fi - 1) + j;
pii tmp = getCoordinate(now, 1);
printf("%2d ", _Gv[tmp.fi][tmp.se]);
} puts("");
}
}
void printTable(pii _table[], int _vertex[]) {
// printGhGvScheduing();
for (int i = 0; i < 200; i++) for (int j = 0; j < 200; j++) mp[i][j] = ' ';
for (int w = 0; w < modulesNum; w++) {
int idx = _vertex[w];
int x = dpV[0] - dpV[getCoordinate(idx, 1).fi];
int y = dpH[0] - dpH[getCoordinate(idx, 0).fi];
int height = _table[idx].se ? input[_table[idx].fi].fi : input[_table[idx].fi].se;
int width = _table[idx].se ? input[_table[idx].fi].se : input[_table[idx].fi].fi;
// printf("_table[i].fi = %2d, height = %2d, width = %2d, x = %2d, y = %2d\n", _table[i].fi, height, width, x, y);
draw(y, x, y, x + width);
draw(y + height, x, y + height, x + width);
draw(y, x, y + height, x);
draw(y, x + width, y + height, x + width);
string val = int2string(w);
for (int i = 0; i < val.size(); i++) {
mp[y + height / 2][x + width / 2 + i] = val[i];
}
printf("%d %d\n", x, y); // 记录每个模块左下角的坐标
}
printMP();
}
void initPlaneSize() {
if (modulesNum > 300) {
shape.fi = shape.se = 2 * (int)sqrt(modulesNum + 0.5);
} else if (modulesNum > 200) {
shape.fi = shape.se = 30;
} else if (modulesNum > 100) {
shape.fi = shape.se = 25;
} else if (modulesNum > 50) {
shape.fi = shape.se = 15;
} else if (modulesNum > 20) {
shape.fi = shape.se = 10;
} else {
shape.fi = shape.se = 8; // 8
}
vNum = (int)(shape.fi * shape.se * 1.5) >> 1;
// vNum = 1000;
}
void initGraphVertex() {
Vh.resize(vNum);
Vv.resize(vNum);
graphH.resize(vNum);
graphV.resize(vNum);
vNum = (shape.se * shape.se + 1) / 2 + 1 ;
bool cloIsOdd = shape.se & 1;
Vh[0] = Vv[0] = make_pair(-1,-1);
// 源点
for (int i = 1; i <= shape.fi + 1>> 1; ++i) {
graphH[0].emplace_back(i);
graphV[0].emplace_back(i);
}
int v = 1;
int vn = shape.se >> 1;
for (int i = 0; i < shape.fi; ++i) {
int st = (i & 1) ? 0 : 1;
bool rowIsOdd = i & 1;
for (int j = st; j < shape.se; j += 2) {
Vh[v].fi = i;
Vh[v].se = j;
mGh[Vh[v]] = v;
if (i == shape.fi - 1) {
graphH[v++].emplace_back(vNum); // 汇点
continue;
}
// left up child
if (j != 0) {
graphH[v].emplace_back((!cloIsOdd & rowIsOdd) ? (v + vn - 1) : (v + vn));
}
// right up child
if (j != shape.se - 1) {
graphH[v].emplace_back((!cloIsOdd & rowIsOdd) ? (v + vn) : (v + vn + 1));
}
v++;
}
}
v = 1;
vn = (int)shape.fi >> 1;
bool rowIsOdd = shape.fi & 1;
for (int i = 0; i < shape.se; ++i) {
int st = (i & 1) ? 1 : 0;
bool _cloIsOdd = i & 1;
for (int j = st; j < shape.fi; j += 2) {
Vv[v].fi = j;
Vv[v].se = i;
mGv[Vv[v]] = v;
if (i == shape.se - 1) {
graphV[v++].emplace_back(vNum);
continue;
}
// down right child
if (j != 0) {
graphV[v].emplace_back((!_cloIsOdd & !rowIsOdd) ? (v + vn - 1) : (v + vn));
}
// up right child
if (j != shape.se - 1) {
graphV[v].emplace_back((!_cloIsOdd & !rowIsOdd) ? (v + vn) : (v + vn + 1));
}
v++;
}
}
for (int i = 0; i < MX*MX; ++i) {
table[i] = make_pair(-1, 0);
}
}
bool isOdd(int x) {
return x & 1;
}
// find the longest path
int dfsH(int _Gh[][MX], int u) {
if (u == vNum) return 0;
if (dpH[u] != -1) {
return dpH[u];
}
int mx = -1;
for (auto it : graphH[u]) {
mx = max(mx, _Gh[u][it] + dfsH(_Gh, it));
}
return dpH[u] = mx;
}
int dfsV(int _Gv[][MX], int u) {
if (u == vNum) return 0;
if (dpV[u] != -1) {
return dpV[u];
}
int mx = -1;
for (auto it : graphV[u]) {
mx = max(mx, _Gv[u][it] + dfsV(_Gv, it));
}
return dpV[u] = mx;
}
pii index2Coordinate(int idx) {
return make_pair(idx / (shape.fi - 1), idx % (shape.fi - 1));
}
/**
* @description: return corresponding coordinate
* @param : idx: table index
* p : 0 = Vh, 1 = Vv
* @return: first : source vertex
* second: sink vertex
*/
pii getCoordinate(int idx, int p) {
int row = idx / (shape.fi - 1);
int col = idx % (shape.fi - 1);
if (p == 0) { // 判断Gh边是往左还是往右,奇数往右,偶数往左
if (isOdd(row + col)) {
return make_pair(mGh[make_pair(row, col)], mGh[make_pair(row + 1, col + 1)]);
} else {
return make_pair(mGh[make_pair(row, col + 1)], mGh[make_pair(row + 1, col)]);
}
} else { // 判断Gv边是往上还是往下,奇数往下,偶数往上
if (isOdd(row + col)) {
return make_pair(mGv[make_pair(row + 1, col)], mGv[make_pair(row, col + 1)]);
} else {
return make_pair(mGv[make_pair(row, col)], mGv[make_pair(row + 1, col + 1)]);
}
}
}
int segment_distance(int a0, int b0, int a1, int b1) {
return max(a0 - b1, a1 - b0);
}
double getCost(int area) {
int sumD = 0;
// printTable();
for (auto it : close) {
int u = vertex[it.fi], v = vertex[it.se];
int height = table[u].se ? input[table[u].fi].fi : input[table[u].fi].se;
int width = table[u].se ? input[table[u].fi].se : input[table[u].fi].fi;
int x0Left = dpV[0] - dpV[getCoordinate(u, 1).fi];
int x0Right = dpV[0] - dpV[getCoordinate(u, 1).fi] + width;
int y0Up = dpH[0] - dpH[getCoordinate(u, 0).fi] + height;
int y0Down = dpH[0] - dpH[getCoordinate(u, 0).fi];
height = table[v].se ? input[table[v].fi].fi : input[table[v].fi].se;
width = table[v].se ? input[table[v].fi].se : input[table[v].fi].fi;
int x1Left = dpV[0] - dpV[getCoordinate(v, 1).fi];
int x1Right = dpV[0] - dpV[getCoordinate(v, 1).fi] + width;
int y10Up = dpH[0] - dpH[getCoordinate(v, 0).fi] + height;
int y1Down = dpH[0] - dpH[getCoordinate(v, 0).fi];
int dx = segment_distance(x0Left, x0Right, x1Left, x1Right);
int dy = segment_distance(y0Down, y0Up, y1Down, y10Up);
assert(dx < 0 && dy < 0);
sumD += dx * dx + dy * dy;
}
return (area + sumD) * 1.0 / sumArea;
}
void SA(double coe) {
memset(Gh, 0, sizeof Gh);
memset(Gv, 0, sizeof Gv);
for (int i = 0; i < MX*MX; i++) {
if (table[i].fi != -1) {
pii edgeH = getCoordinate(i, 0);
pii edgeV = getCoordinate(i, 1);
Gh[edgeH.fi][edgeH.se] = table[i].se ? input[table[i].fi].fi : input[table[i].fi].se;
Gv[edgeV.fi][edgeV.se] = table[i].se ? input[table[i].fi].se : input[table[i].fi].fi;
}
}
// printTable();
memset(dpH, -1, sizeof dpH);
memset(dpV, -1, sizeof dpV);
int m1 = dfsH(Gh, 0), m2 = dfsV(Gv, 0);
double oldArea = m1 * m2;
double oldCost = getCost(oldArea);
double t = 3000.0; // Initial temperature
while (t > 1e-14) {
// 随机挑选两个位置
pii loc1 = make_pair(rand() % ((shape.fi - 1) * (shape.fi - 1)), rand() % 1);
pii loc2 = make_pair(rand() % ((shape.fi - 1) * (shape.fi - 1)), rand() % 1);
/**
* 以下情况直接continue
* 1. 两个位置都为空
* 2. 两个位置一样,且角度也一样
* 3. 两个位置一样,角度不一样,但是模块是一个正方形
*/
if (table[loc1.fi].fi == -1 && table[loc2.fi].fi == -1 || loc1 == loc2
|| loc1.fi == loc2.fi && input[table[loc1.fi].fi].fi == input[table[loc1.fi].fi].se) {
continue;
}
changeSum++;
// 旋转某一个模块 (特判下)
if (loc1.fi == loc2.fi) {
// 保存 交换之前的部分值
pii tableValue1 = table[loc1.fi];
pii edge1H = getCoordinate(loc1.fi, 0);
pii edge1V = getCoordinate(loc1.fi, 1);
pii val1 = make_pair(Gh[edge1H.fi][edge1H.se], Gv[edge1V.fi][edge1V.se]);
// 模块位置未变化所以不用改变vertex
// vertex[table[loc1.fi].fi]
// 更改当前的状态(只需要姿势即可),并且计算出 新的 面积
table[loc1.fi].se = (table[loc1.fi].se^1);
Gh[edge1H.fi][edge1H.se] = table[loc1.fi].se ? input[tableValue1.fi].fi : input[tableValue1.fi].se;
Gv[edge1V.fi][edge1V.se] = table[loc1.fi].se ? input[tableValue1.fi].se : input[tableValue1.fi].fi;
// 清空记忆化数组
memset(dpH, -1, sizeof dpH);
memset(dpV, -1, sizeof dpV);
m1 = dfsH(Gh, 0), m2 = dfsV(Gv, 0);
double newArea = m1 * m2;
double newCost = getCost(newArea);
if (newCost < finalCost) {
finalHeight = m1;
finalwidth = m2;
finalCost = newCost;
memcpy(finalTable, table, sizeof(table));
memcpy(finalGh, Gh, sizeof(Gh));
memcpy(finalGv, Gv, sizeof(Gv));
memcpy(finalVertex, vertex, sizeof(vertex));
}
// printTable();
double delta = newCost - oldCost; // delta area
if (delta < 0) { // 新解分配面积更小,直接接受
oldCost = newCost;
} else if (exp(-delta / t) * 32767 > rand()) { // 新解更次,以一定概率接受
// int t1 = exp(-delta / t) * 32767;
oldCost = newCost;
} else { // 不接受,还原状态
table[loc1.fi] = tableValue1;
Gh[edge1H.fi][edge1H.se] = val1.fi;
Gv[edge1V.fi][edge1V.se] = val1.se;
}
t *= coe; //根据牛顿冷却定律降温
continue;
}
// 保存 交换之前的部分值
pii tableValue1 = table[loc1.fi];
pii tableValue2 = table[loc2.fi];
pii edge1H = getCoordinate(loc1.fi, 0);
pii edge1V = getCoordinate(loc1.fi, 1);
pii edge2H = getCoordinate(loc2.fi, 0);
pii edge2V = getCoordinate(loc2.fi, 1);
pii val1 = make_pair(Gh[edge1H.fi][edge1H.se], Gv[edge1V.fi][edge1V.se]);
pii val2 = make_pair(Gh[edge2H.fi][edge2H.se], Gv[edge2V.fi][edge2V.se]);
// 更改当前的状态并且计算出 新的 面积
table[loc1.fi] = tableValue2;
table[loc2.fi] = tableValue1;
vertex[tableValue1.fi] = loc2.fi;
vertex[tableValue2.fi] = loc1.fi;
if (tableValue2.fi != -1) { // 把loc2的room里的module放到loc1中,且loc2不为空
Gh[edge1H.fi][edge1H.se] = tableValue2.se ? input[tableValue2.fi].fi : input[tableValue2.fi].se;
Gv[edge1V.fi][edge1V.se] = tableValue2.se ? input[tableValue2.fi].se : input[tableValue2.fi].fi;
} else {
Gh[edge1H.fi][edge1H.se] = 0;
Gv[edge1V.fi][edge1V.se] = 0;
}
if (tableValue1.fi != -1) { // 把loc1的room里的module放到loc2中,且loc1不为空
Gh[edge2H.fi][edge2H.se] = tableValue1.se ? input[tableValue1.fi].fi : input[tableValue1.fi].se;
Gv[edge2V.fi][edge2V.se] = tableValue1.se ? input[tableValue1.fi].se : input[tableValue1.fi].fi;
} else {
Gh[edge2H.fi][edge2H.se] = 0;
Gv[edge2V.fi][edge2V.se] = 0;
}
// 清空记忆化数组
memset(dpH, -1, sizeof dpH);
memset(dpV, -1, sizeof dpV);
m1 = dfsH(Gh, 0), m2 = dfsV(Gv, 0);
double newArea = m1 * m2;
// printTable();
double newCost = getCost(newArea);
if (newCost < finalCost) {
finalHeight = m1;
finalwidth = m2;
finalCost = newCost;
memcpy(finalTable, table, sizeof(table));
memcpy(finalGh, Gh, sizeof(Gh));
memcpy(finalGv, Gv, sizeof(Gv));
memcpy(finalVertex, vertex, sizeof(vertex));
}
double delta = newCost - oldCost; // delta area
if (delta < 0) { // 新解分配面积更小,直接接受
oldCost = newCost;
} else if (exp(-delta / t) * 32767 > rand()) { // 新解更次,以一定概率接受
// int t1 = exp(-delta / t) * 32767;
oldCost = newCost;
} else { // 不接受,还原状态
table[loc1.fi] = tableValue1;
table[loc2.fi] = tableValue2;
vertex[tableValue1.fi] = loc1.fi;
vertex[tableValue2.fi] = loc2.fi;
Gh[edge1H.fi][edge1H.se] = val1.fi;
Gv[edge1V.fi][edge1V.se] = val1.se;
Gh[edge2H.fi][edge2H.se] = val2.fi;
Gv[edge2V.fi][edge2V.se] = val2.se;
}
t *= coe; //根据牛顿冷却定律降温
}
}
void printBestResult() {
memset(dpH, -1, sizeof dpH);
memset(dpV, -1, sizeof dpV);
int height = dfsH(finalGh, 0);
int width = dfsV(finalGv, 0);
int newArea = height * width;
printf("Total time (ms) : %.2f\n",(double)(endClock - startClock) * 1000 / CLOCKS_PER_SEC);
printf("The sum changes : %d\n",changeSum);
printf("The minimum area : %d\n",newArea);
printf("Area of waste : %d\n",newArea - sumArea);
printf("Utilization rate : %.2f%%\n", (double)(sumArea * 1.0 / newArea) * 100);
printf("height = %d width = %d\n",height, width);
// for (int i = 0; i < modulesNum; i++) {
// printf("%d: %d %d\n", i, input[i].fi, input[i].se);
// }
printf("\n -------- final state following begin -------- \n");
printTable(finalTable, finalVertex);
printf("\n -------- final state following end -------- \n");
}
/**
* FIXED: 1. 可以实现多矩形分配,占比率约为90%
* 2. 最终的模块可以分配展示出来,而不是手动去画
*
* TODO:
* 1. 调参过程需要加入自动化, 可以引入多线程去调参
* 2. 当有两个模块需要靠近时,该如何处理
* 3. 处理不规则模块,例如 L-shape
* 4. 处理软模块
*/
void initTable() {
for (int i = 0; i < modulesNum; i++) {
table[i].fi = i;
table[i].se = rand() % 1;
vertex[i] = i;
}
}
void printRes() {
for (int i = 0; i < modulesNum; i++) {
int u = vertex[i];
int x = dpV[0] - dpV[getCoordinate(u, 1).fi];
int y = dpH[0] - dpH[getCoordinate(u, 0).fi];
printf("%d %d\n", x, y);
}
}
int main() {
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
srand(11113);
startClock=clock();
cin >> modulesNum;
initPlaneSize();
initGraphVertex();
finalCost = 9999999;
for (int i = 0; i < modulesNum; ++i) {
double x,y;
cin >> x >> y;
int xx = (int)(x) + 1;
int yy = (int)(y) + 1;
sumArea += x * y;
input.emplace_back(make_pair(xx,yy));
}
cin >> closeNum;
for (int i = 0; i < closeNum; i++) {
int x, y; cin >> x >> y;
close.emplace_back(make_pair(x, y));
}
initTable();
for (int i = 0; i < 10; i++) {
SA(coe[rand()%10]);
}
endClock=clock();
printBestResult();
// printRes();
return 0;
}
Judge代码参考
import argparse
import matplotlib.pyplot as plt
def parse_argument():
parser = argparse.ArgumentParser(description='JUDGER of the problem - '
'"floorplanning with optimum area utilization under constraints"')
parser.add_argument('input_path', help='the input file path')
parser.add_argument('output_path', help='the output file path')
args = parser.parse_args()
return args
def read_input_file(input_path):
with open(input_path) as input_file:
n = int(input_file.readline().strip())
cells = []
for _ in range(n):
w, h = (float(_) for _ in input_file.readline().strip().split(' '))
cells.append((w, h))
m = int(input_file.readline().strip())
nears = []
for _ in range(m):
a, b = (int(_) for _ in input_file.readline().strip().split(' '))
nears.append((a, b))
return n, cells, m, nears
def read_output_file(output_path):
coors = []
with open(output_path) as output_file:
for line in output_file:
x, y = (float(_) for _ in line.strip().split(' '))
coors.append((x, y))
return coors
def segment_distance(a0, b0, a1, b1):
return max(a0 - b1, a1 - b0)
def display_placement(n, x0_lst, x1_lst, y0_lst, y1_lst, nears):
fig, ax = plt.subplots()
for i in range(n):
x0, x1, y0, y1 = x0_lst[i], x1_lst[i], y0_lst[i], y1_lst[i]
rect = plt.Rectangle((x0, y0), x1-x0, y1-y0, lw=1.0, ls='-', fc='blue', ec='black')
ax.add_patch(rect)
ax.annotate(str(i), ((x0 + x1) / 2.0, (y0 + y1) / 2.0),
color='w', weight='bold', fontsize=6, ha='center', va='center')
for (i, j) in nears:
x0a, x1a, y0a, y1a = x0_lst[i], x1_lst[i], y0_lst[i], y1_lst[i]
x0b, x1b, y0b, y1b = x0_lst[j], x1_lst[j], y0_lst[j], y1_lst[j]
plt.plot([(x0a + x1a) / 2.0, (x0b + x1b) / 2.0 ],
[(y0a + y1a) / 2.0, (y0b + y1b) / 2.0])
min_x, max_x = min(x0_lst), max(x1_lst)
min_y, max_y = min(y0_lst), max(y1_lst)
aix_range = max(max_x - min_x, max_y - min_y) * 1.1
center_x = (min_x + max_x) / 2.0
center_y = (min_y + max_y) / 2.0
ax.set_xlim([center_x - aix_range / 2.0, center_x + aix_range / 2.0])
ax.set_ylim([center_y - aix_range / 2.0, center_y + aix_range / 2.0])
plt.show()
def main():
# get input and output file path
args = parse_argument()
# read input and output files
n, cells, m, nears = read_input_file(args.input_path)
coors = read_output_file(args.output_path)
x0_lst = [coor[0] for coor in coors]
x1_lst = [coor[0] + cell[0] for (coor, cell) in zip(coors, cells)]
y0_lst = [coor[1] for coor in coors]
y1_lst = [coor[1] + cell[1] for (coor, cell) in zip(coors, cells)]
# displace placement
display_placement(n, x0_lst, x1_lst, y0_lst, y1_lst, nears)
# check if cells overlap
for i in range(n-1):
for j in range(i+1, n):
dx = segment_distance(x0_lst[i], x1_lst[i], x0_lst[j], x1_lst[j])
dy = segment_distance(y0_lst[i], y1_lst[i], y0_lst[j], y1_lst[j])
if dx < 0 and dy < 0:
raise Exception(f"The cell[{i}] and cell[{j}] overlapped.")
# calculate the bounding rect area
min_x, max_x = min(x0_lst), max(x1_lst)
min_y, max_y = min(y0_lst), max(y1_lst)
area = (max_x - min_x) * (max_y - min_y)
# calculate the dist scores
dist = 0
for (i, j) in nears:
dx = segment_distance(x0_lst[i], x1_lst[i], x0_lst[j], x1_lst[j])
dy = segment_distance(y0_lst[i], y1_lst[i], y0_lst[j], y1_lst[j])
ds = (dx if dx > 0 else 0) + (dy if dy > 0 else 0)
dist += ds * ds
norm = sum((x1 - x0) * (y1 - y0) for x0, x1, y0, y1 in zip(x0_lst, x1_lst, y0_lst, y1_lst))
score = (area + dist) / norm
print("%.3f" % score)
if __name__ == "__main__":
main()
首先是比赛关于此题的总结,总体来说还算比较充分,基本赛前准备和赛题较吻合,当然由于自己的debug能力,导致这道题拖到了4h后才交,好在最后这道题的成绩也是满分,也可能是样例比较水吧,同时也侧面反映出了自己的编码、找错能力下滑了很多。
然后是这道题的拓展,总的来看题目给的约束很松,很多约束没有涉及到,比如说提前预制某个模块在某个位置,有一些模块时软模块,等等。说实话,在赛前一直和队友讨论如何解决模块预制问题,一直没有好的解决方案,因为最终的布置图是未知的,所以没办法提前在某个区域预制模块,但是论文中提到了这一点,并且给出了相应布置图,但是具体的解决方案没有详细说,如果哪位大佬看到这里并且有好的解决方案,麻烦告知下,谢谢。
在code上我觉得还有很多地方可以优化,比如可以进入多线程进行自动调参,毕竟是个随机算法嘛,说不定某个参数就可以找个更优的结果,想进一步讨论也可以私信我。
另外真的好久不写blog了,不知道该如何描述算法或者解决方案,可能会引起极度不适,东一头西一头的,见谅啦。加油陌生人~