------------------------核心---------------------
deploy.h
#ifndef __ROUTE_H__
#define __ROUTE_H__
#include "lib_io.h"
#include "lib_time.h"
#include "string.h"
#include "stdlib.h"
#include "time.h"
//#include <sys/time.h>
#include "iostream"
#include "fstream"
#include "vector"
#include "queue"
#include "algorithm"
#include "map"
#include "set"
#include "sstream"
using namespace std;
void deploy_server(char * graph[MAX_EDGE_NUM], int edge_num, char * filename);
#define INF 2000000000
#define INT_MAX 2000000000
#define INT_MAX1 2000000000
#define MAX_netNodeCnt 1002 // 网络节点最大数+2
#define MAX_consumeNodeCnt 500 // 消费节点最大数
#define MAX_linkeCnt 20000 // 链路最大数
struct Edge
{
//int from; // 链路起始节点ID
int to; // 链路终止节点ID
int cap; // 总带宽
int cost; // 单位带宽租用费
int flow;
int next;
};
// 消费节点信息
struct ConsumeNode
{
int ID; // 消费节点ID
int netID; // 相连网络节点ID
int demand; // 视频带宽需求
};
// 用于结果输出
struct Result
{
vector<int> path; // 路径
int flow; // 占用带宽
Result()
{
path.clear();
flow = 0;
}
};
struct Node
{
int id; // 网络节点ID
int degree; // 度
Node()
{
id = 0;
degree = 0;
}
bool operator < (const struct Node &node)const
{
return degree > node.degree;
}
};
void build(char * topo[MAX_EDGE_NUM], int line_num);
void addEdge(int from, int to, int cap, int cost, int &id);
bool min_cost(int &cost, int nEdge, int nServer); // 最小费用流
void record_result();
void dfs(int u, Result &r, int &flow); // 搜索路径
bool min_cost1();
void record_result1();
///随机算法/
// 个体
class Entity
{
public:
set<int> position; // 服务器安放的位置
int fitness; // 适应度值,即总的花费
Entity()
{
fitness = INT_MAX;
position.clear();
}
bool operator < (const Entity &e) const
{
return fitness < e.fitness;
}
int getFit();
int getFit1();
};
void init(); // 种群初始化
void mutate(); // 变异函数
void mainEA(); // 随机算法主函数
#endif
deploy.cpp
#include "deploy.h"
#include "algorithm"
#include <stdio.h>
int netNodeCnt; // 网络节点数量
int linkeCnt; // 网络链路数量
int consumeNodeCnt; // 消费节点数量
int serverCost; // 视频内容服务器部署成本
ConsumeNode consume[MAX_consumeNodeCnt]; // 消费节点信息数组
Edge edge[4*MAX_linkeCnt]; // 链路信息数组
int head[MAX_netNodeCnt];
int preV[MAX_netNodeCnt];
int preE[MAX_netNodeCnt];
int dist[MAX_netNodeCnt];
bool vis[MAX_netNodeCnt];
int id = 0; // 边的ID
int S, T; // 超级源点、汇点
int F; // 总带宽需求
int minCost = INT_MAX; // 最小花费
map<int, int> mapConsume; // 记录网络节点与那个消费节点相连
vector<Result> path; // 路径
vector<Result> bestPath; // 最佳路径
string result;
Entity Best; // 最优解个体
double PMUTATION1 = 55;
double PMUTATION2 = 90;
int POPSIZE = 50;
set<int> sChoice; // 服务器选择范围
vector<int> vChoice;
set<int> bad; // 禁忌表
int maxflow;
int serverNum;
int cur[MAX_netNodeCnt], pre[MAX_netNodeCnt];
clock_t start, finish;
Node node[MAX_netNodeCnt];
//struct timeval T1, T2, T3;
double decs = 0.97;
double temp = 150;
double thold = 15;
Entity MostBest;
void solve();
//You need to complete the function
void deploy_server(char * topo[MAX_EDGE_NUM], int line_num, char * filename)
{
//gettimeofday(&T1, NULL);
build(topo, line_num);
srand(time(NULL));
//gettimeofday(&T2, NULL);
mainEA();
cout << "minCost:" << minCost << endl;
record_result();
//write_result(result.c_str(), filename);
}
void addEdge(int from, int to, int cap, int cost, int &id)
{
edge[id].to = to;
edge[id].cap = cap;
edge[id].cost = cost;
edge[id].flow = 0;
edge[id].next = head[from];
head[from] = id++;
}
// 读入文件,建图
void build(char * topo[MAX_EDGE_NUM], int line_num)
{
memset(head, -1, sizeof(head));
istringstream sin;
sin.str(topo[0]);
sin >> netNodeCnt >> linkeCnt >> consumeNodeCnt;
sin.str(topo[2]);
sin >> serverCost;
// 网络节点之间的边
int i, from, to, cap, cost;
for(i=4; i<linkeCnt+4; i++)
{
sin.str(topo[i]);
sin >> from >> to >> cap >> cost;
addEdge(from, to, cap, cost, id);
addEdge(to, from, 0, -cost, id);
addEdge(to, from, cap, cost, id);
addEdge(from, to, 0, -cost, id);
//cout << from << " " << to << " " << cap << " " << cost << endl;
node[from].id = from;
node[from].degree++;
node[to].id = to;
node[to].degree++;
}
S = netNodeCnt;
T = S + 1;
// 超级源点与“消费节点连向的网络节点”之间的边
for(i=0; i<consumeNodeCnt; i++)
{
sin.str(topo[i+linkeCnt+5]);
sin >> consume[i].ID >> consume[i].netID >> consume[i].demand;
addEdge(S, consume[i].netID, consume[i].demand, 0, id);
addEdge(consume[i].netID, S, 0, 0, id);
F += consume[i].demand;
mapConsume[consume[i].netID] = consume[i].ID;
}
}
// 记录结果
void record_result()
{
ostringstream sout;
int n = bestPath.size();
sout << n << "\n" << "\n";
for (int p = 0; p < n; p++)
{
int j;
for(j = bestPath[p].path.size()-2; j >=0; j--)
{
sout << bestPath[p].path[j] << " ";
}
sout << mapConsume[bestPath[p].path[0]] << " " << bestPath[p].flow;
if(p != n-1)
sout << "\n";
}
result = sout.str();
//cout << result << endl;
}
// 最小费用流
bool min_cost(int &cost, int nEdge, int nServer)
{
queue<int> q;
int flow = 0;
cost = serverCost * nServer;
int i;
for(i=0; i<nEdge; i++)
edge[i].flow = 0;
path.clear();
while(1)
{
memset(vis, false, sizeof(vis));
fill(dist, dist+netNodeCnt+2, INT_MAX);
dist[S] = 0;
q.push(S);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(i=head[u]; i!=-1; i=edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && dist[v] > dist[u]+edge[i].cost)
{
dist[v] = dist[u] + edge[i].cost;
preV[v] = u;
preE[v] = i;
if(!vis[v])
{
q.push(v);
vis[v] = true;
}
}
}
}
if(dist[T] == INT_MAX)
break;
int f = INT_MAX;
int u;
for(u=T; u!=S; u=preV[u])
{
int i = preE[u];
if (edge[i].cap - edge[i].flow < f)
f = edge[i].cap - edge[i].flow;
}
for(u=T; u!=S; u=preV[u])
{
int i = preE[u];
edge[i].flow += f;
edge[i^1].flow -= f;
}
flow += f;
cost += dist[T] * f;
if(cost > minCost)
return false;
}
if (flow >= F)
{
if (cost < minCost)
{
minCost = cost;
bestPath.clear();
Result r;
int f = 0;
while(f < F)
{
int flow = INT_MAX;
dfs(S, r, flow);
f += flow;
}
}
return true;
}
return false;
}
// 搜索路径
void dfs(int u, Result &r, int &flow)
{
if(u == T)
{
r.flow = flow;
for (int k = T; k != S; k = preV[k])
{
int i = preE[k];
edge[i].flow -= flow;
}
bestPath.push_back(r);
return;
}
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v = edge[i].to;
if(edge[i].flow > 0)
{
preV[v] = u;
preE[v] = i;
r.path.push_back(v);
if(edge[i].flow < flow)
flow = edge[i].flow;
dfs(v, r, flow);
r.path.pop_back();
return;
}
}
}
/zkw
int min(int a, int b)
{
return a < b ? a : b;
}
// KM重标号
bool adjust()
{
int v, min = INF;
for (int i = 0; i < netNodeCnt+2; i++)
{
if (!vis[i])
continue;
for (int j = head[i]; j != -1; j = edge[j].next)
{
v = edge[j].to;
if (edge[j].cap > edge[j].flow)
if (!vis[v] && dist[v] - dist[i] + edge[j].cost < min)
min = dist[v] - dist[i] + edge[j].cost;
}
}
if (min == INF)
return false;
for (int i = 0; i <netNodeCnt+2; i++)
if (vis[i])
vis[i] = false, dist[i] += min;
return true;
}
// 增广
int augment(int i, int flow)
{
if (i == T)
{
minCost += dist[S] * flow;
maxflow += flow;
return flow;
}
vis[i] = true;
for (int j = head[i], v; v = edge[j].to, j != -1; j = edge[j].next)
{
if (edge[j].cap > edge[j].flow)
{
if (vis[v] || dist[v] + edge[j].cost != dist[i])
continue;
int delta = augment(v, min(flow, edge[j].cap - edge[j].flow));
if (delta)
{
edge[j].flow += delta;
edge[j ^ 1].flow -= delta;
return delta;
}
}
}
return 0;
}
bool zkw(int &fit, int idx, int n)
{
for(int i=0; i<idx; i++)
edge[i].flow = 0;
memset(dist, 0, sizeof(dist));
minCost = maxflow = 0;
for (int i = 0; i < netNodeCnt; i++)
{
vis[i] = false;
}
do
{
while (augment(S, INF))
memset(vis, false, sizeof(vis));
} while (adjust());
if (maxflow < F)
return false;
fit = minCost + n * serverCost;
// 搜路径
bestPath.clear();
Result r;
int f = 0;
while(f < F)
{
int flow = INT_MAX;
dfs(S, r, flow);
f += flow;
}
return true;
}
/初始化时用到
// 最小费用流
bool min_cost1()
{
queue<int> q;
int flow = 0;
path.clear();
while(1)
{
memset(vis, false, sizeof(vis));
fill(dist, dist+netNodeCnt+2, INT_MAX);
dist[S] = 0;
q.push(S);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && dist[v] > dist[u]+edge[i].cost)
{
dist[v] = dist[u] + edge[i].cost;
preV[v] = u;
preE[v] = i;
if(!vis[v])
{
q.push(v);
vis[v] = true;
}
}
}
}
if(dist[T] == INT_MAX)
break;
int f = INT_MAX;
int u;
for(u=T; u!=S; u=preV[u])
{
int i = preE[u];
if (edge[i].cap - edge[i].flow < f)
f = edge[i].cap - edge[i].flow;
}
for(u=T; u!=S; u=preV[u])
{
int i = preE[u];
edge[i].flow += f;
edge[i^1].flow -= f;
}
flow += f;
}
bestPath.clear();
Result r;
int f = 0;
while(f < F)
{
int flow = INT_MAX1;
dfs(S, r, flow);
f += flow;
}
return true;
}
// 记录结果
void record_result1(set<int> &s)
{
int n = bestPath.size();
for (int p = 0; p < n; p++)
{
s.insert(bestPath[p].path[bestPath[p].path.size()-2]);
}
set<int>::iterator it;
for(it = s.begin(); it != s.end(); it++)
{
vChoice.push_back(*it);
}
}
随机算法//
int Entity::getFit1()
{
map<int, int> m;
int cnt = position.size();
int fit = 0;
// 保存之前的图
set<int>::iterator it;
for(it = position.begin(); it != position.end(); it++)
m[*it] = head[*it];
// 添加一些新边:放置服务器的网络节点连向超级汇点
int idx = id;
for (it = position.begin(); it != position.end(); it++)
{
addEdge(*it, T, INT_MAX, 0, idx);
addEdge(T, *it, 0, 0, idx);
}
bool find = zkw(fit, idx, position.size());
// 恢复之前的图
for (it = position.begin(); it != position.end(); it++)
head[*it] = m[*it];
head[T] = -1;
fitness = find ? fit : INT_MAX;
return fitness;
}
int Entity::getFit()
{
map<int, int> m;
int cnt = position.size();
int fit = 0;
// 保存之前的图
set<int>::iterator it;
for(it = position.begin(); it != position.end(); it++)
m[*it] = head[*it];
// 添加一些新边:放置服务器的网络节点连向超级汇点
int idx = id;
for (it = position.begin(); it != position.end(); it++)
{
addEdge(*it, T, INT_MAX, 0, idx);
addEdge(T, *it, 0, 0, idx);
}
bool find = min_cost(fit, idx, position.size());
// 恢复之前的图
for (it = position.begin(); it != position.end(); it++)
head[*it] = m[*it];
head[T] = -1;
fitness = find ? fit : INT_MAX;
return fitness;
}
// 初始化
void init()
{
// 将所有的节点与汇点连边,费用为服务器费用
// 挑选度最大的2个点、带宽需求最高的2个点作为服务器
// 与汇点连边的费用为0
// 搜索最小费用路径,将所有倒数第2个点作为服务器,加入sChoice中
// 之后的搜索中,所有服务器都来自于sChoice
set<int> s;
for(int k=0; k<netNodeCnt; k++)
s.insert(k);
set<int> s1;
s1.insert(node[0].id); // 度最高的2个点
s1.insert(node[1].id);
Node node1[MAX_consumeNodeCnt];
for(int k=0; k<consumeNodeCnt; k++)
{
node1[k].id = consume[k].netID;
node1[k].degree = consume[k].demand;
}
sort(node1, node1+consumeNodeCnt);
s1.insert(node1[0].id); // 带宽需求最高的2个点
s1.insert(node1[1].id);
// 保存之前的图
map<int, int> m;
set<int>::iterator it;
for(int k = 0; k < netNodeCnt; k++)
m[k] = head[k];
for(it=s1.begin(); it != s1.end(); it++)
{
s.erase(*it);
}
int idx = id;
for(it=s.begin(); it != s.end(); it++)
{
addEdge(*it, T, INT_MAX1, serverCost, idx);
addEdge(T, *it, 0, 0, idx);
}
for(it=s1.begin(); it != s1.end(); it++)
{
addEdge(*it, T, INT_MAX1, 0, idx);
addEdge(T, *it, 0, 0, idx);
}
min_cost1();
record_result1(sChoice);
// 恢复之前的图
for (int k=0; k<netNodeCnt; k++)
head[k] = m[k];
head[T] = -1;
start = clock();
Best.position = sChoice;
Best.getFit1();
}
bool accept(double delta, double temper)
{
if(delta <= 0)
{
if(Best.fitness > MostBest.fitness)
MostBest = Best;
return true;
}
//return rand() <= exp((-delta) / temper) * RAND_MAX;
return exp((-delta) / temper) >= 0.1;
}
// 变异函数
void mutate()
{
int r = rand() % 100;
int cnt = Best.position.size();
// 替换变异
if(r < PMUTATION1 && cnt > 0)
{
Entity M = Best;
int a = rand() % vChoice.size(); // 随机选取一个节点
set<int>::iterator it;
vector<int> v;
for(it = M.position.begin(); it != M.position.end(); it++)
v.push_back(*it);
if(bad.count(vChoice[a])) // 如果该节点在禁忌表中
{
if(rand() % 100 < 70) // 以0.3的概率重新选择一个节点
{
int j = rand() % v.size();
M.position.insert(vChoice[a]); // 加进新的
M.position.erase(v[j]); // 删除旧的节点
M.getFit1();
if(accept(M.fitness - Best.fitness, temp))
Best = M;
if(temp >= thold)
temp *= decs;
}
}
else
{
int j = rand() % v.size();
M.position.insert(vChoice[a]); // 加进新的
M.position.erase(v[j]); // 删除旧的节点
M.getFit1();
if(accept(M.fitness - Best.fitness, temp))
Best = M;
if(temp >= thold)
temp *= decs;
}
}
// 删除变异
else if (r < PMUTATION2 && cnt > 0)
{
Entity M = Best;
vector<int> v;
set<int>::iterator it;
for(it = M.position.begin(); it != M.position.end(); it++)
v.push_back(*it);
int i = rand() % cnt; // 随机选择变异个体中的某个服务器,要将其删除
M.position.erase(v[i]); // 除掉旧的
M.getFit1();
if(accept(M.fitness - Best.fitness, temp))
{
Best = M;
if(temp >= thold)
temp *= decs;
bad.insert(v[i]);
}
}
// 增加变异
else
{
Entity M = Best;
int a = rand() % vChoice.size(); // 另外一个服务器
if(!M.position.count(vChoice[a])) // 如果该节点未放置服务器
{
if(bad.count(vChoice[a])) // 如果该节点在禁忌表中
{
if(rand() % 100 < 70) // 以0.3的概率重新选择一个节点
{
M.position.insert(vChoice[a]); // 加进新的
M.getFit1();
if(accept(M.fitness - Best.fitness, temp))
{
Best = M;
if(temp >= thold)
temp *= decs;
}
}
}
else
{
M.position.insert(vChoice[a]); // 加进新的
M.getFit1();
if(accept(M.fitness - Best.fitness, temp))
{
Best = M;
if(temp >= thold)
temp *= decs;
}
}
}
}
}
void mainEA()
{
init();
double t = (double)clock() / CLOCKS_PER_SEC;
//double t = T2.tv_sec - T1.tv_sec;
while (t < 90)
{
mutate();
t = (double)clock() / CLOCKS_PER_SEC;
//t = T2.tv_sec - T1.tv_sec;
if(t > 75)
{
PMUTATION1 = 60;
}
}
if(Best.fitness > MostBest.fitness)
Best = MostBest;
finish = clock();
double duration = (double) (finish-start)/CLOCKS_PER_SEC; //时间以秒为单位
cout << "时间:" << duration << endl;
cout << "bestFit:" << Best.fitness << endl;
cout << Best.position.size() << endl;
}
---------------------------------------主函数---------------------
cdn.cpp
#include "deploy.h"
#include "lib_io.h"
#include "lib_time.h"
#include "stdio.h"
int main(int argc, char *argv[])
{
print_time("Begin");
char *topo[MAX_EDGE_NUM];
int line_num;
char *topo_file = argv[1];
line_num = read_file(topo, MAX_EDGE_NUM, topo_file);
printf("line num is :%d \n", line_num);
if (line_num == 0)
{
printf("Please input valid topo file.\n");
return -1;
}
char *result_file = argv[2];
deploy_server(topo, line_num, result_file);
release_buff(topo, line_num);
print_time("End");
return 0;
}
---------------------------------------非核心---------------------
io.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <sys/timeb.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#define MAX_LINE_LEN 55000
#define INLINE static __inline
#ifdef _DEBUG
#define PRINT printf
#else
#define PRINT(...)
#endif
INLINE void write_file(const bool cover, const char * const buff, const char * const filename);
void print_time(const char *head)
{
//#ifdef _DEBUG
struct timeb rawtime;
struct tm * timeinfo;
ftime(&rawtime);
timeinfo = localtime(&rawtime.time);
static int ms = rawtime.millitm;
static unsigned long s = rawtime.time;
int out_ms = rawtime.millitm - ms;
unsigned long out_s = rawtime.time - s;
ms = rawtime.millitm;
s = rawtime.time;
if (out_ms < 0)
{
out_ms += 1000;
out_s -= 1;
}
printf("%s date/time is: %s \tused time is %lu s %d ms.\n", head, asctime(timeinfo), out_s, out_ms);
//#endif
}
int read_file(char ** const buff, const unsigned int spec, const char * const filename)
{
FILE *fp = fopen(filename, "r");
if (fp == NULL)
{
PRINT("Fail to open file %s, %s.\n", filename, strerror(errno));
return 0;
}
PRINT("Open file %s OK.\n", filename);
char line[MAX_LINE_LEN + 2];
unsigned int cnt = 0;
while ((cnt < spec) && !feof(fp))
{
line[0] = 0;
if (fgets(line, MAX_LINE_LEN + 2, fp) == NULL) continue;
if (line[0] == 0) continue;
buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2);
strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1);
buff[cnt][MAX_LINE_LEN + 1] = 0;
cnt++;
}
fclose(fp);
PRINT("There are %d lines in file %s.\n", cnt, filename);
return cnt;
}
void write_result(const char * const buff,const char * const filename)
{
// 以覆盖的方式写入
write_file(1, buff, filename);
}
void release_buff(char ** const buff, const int valid_item_num)
{
for (int i = 0; i < valid_item_num; i++)
free(buff[i]);
}
INLINE void write_file(const bool cover, const char * const buff, const char * const filename)
{
if (buff == NULL)
return;
const char *write_type = cover ? "w" : "a";//1:覆盖写文件,0:追加写文件
FILE *fp = fopen(filename, write_type);
if (fp == NULL)
{
PRINT("Fail to open file %s, %s.\n", filename, strerror(errno));
return;
}
PRINT("Open file %s OK.\n", filename);
fputs(buff, fp);
fputs("\n", fp);
fclose(fp);
}
lib_io.h
#ifndef __LIB_IO_H__
#define __LIB_IO_H__
#define MAX_EDGE_NUM (2000 * 20)
//读取文件并按行输出到buff。
//buff为一个指针数组,每一个元素是一个字符指针,对应文件中一行的内容。
//spec为允许解析的最大行数。
extern int read_file(char ** const buff, const unsigned int spec, const char * const filename);
//将result缓冲区中的内容写入文件,写入方式为覆盖写入
extern void write_result(const char * const buff,const char * const filename);
//释放读文件的缓冲区
extern void release_buff(char ** const buff, const int valid_item_num);
#endif
lib_time.h
#ifndef __LIB_TIME_H__
#define __LIB_TIME_H__
//打印时间。入参为打印信息头
void print_time(const char * const head);
#endif
高级用例 case0.txt,800节点,3022条边,360个消费者节点
结果:104863