在—个遥远的国家,Sark Mevo 所领导的政党最终击败了Reguel Tekris王子领导的联合党派。Mevo希望巩固他在首都地区的席位。首都由14个街区组成,这些街区将分组为多个选区。下图是首都地区的示意图。在图中用数字1到14对这些街区进行了编号。每个街区中的另外两个数字是预计该街区会投票给Mevo的选民数和该街区的选民总数。所有选民都必须投票,且选举胜出方必须得到绝对多数选票。一个选区可以由多个相邻的街区组成,且选区内总选民数应在30,000到100,000之间。如果两个街区不相邻,例如12和13,则它们不能组成一个选区。如果某个街区选民人数不少于50,000,则允许此街区单独作为一个选区。但是由于Mevo本人就居住在街区10内,因此迫于舆论压力,他不能将这个街区单独作为一个选区。
请设计出一个将首都划分为5个选区的方案,以使Mevo得到的席位数最多。如果这样做有困难,可以尝试划分为6个选区。
我写的程序(加了不少的注释,呵呵):
本程序在G++环境下编译通过
功能描述:
此程序首先确定所有的选区,进而确定了所有可能的选区划分方案,并最终找出了最大席位数和最佳划分方案。
*/
#include < iostream >
using namespace std;
const int MaxN = 80 ; /**/ /*街区数目上限*/
const int MAX_POP = 100000 ; /**/ /*选区人数上限*/
const int MIN_POP = 30000 ; /**/ /*选区人数下限*/
const int MIN_SINGLE = 50000 ; /**/ /*单个街区作为一个选区人数下限*/
/**/ /*数据类型:记录一个选区或街区的人数和支持人数*/
typedef struct t_info ... {
int num, sup;
} ;
/**/ /*数据类型:记录每个街区得相邻街区*/
typedef struct t_edge ... {
int v;
t_edge *next;
} ;
/**/ /*数据类型:记录每个街区信息*/
typedef struct t_node ... {
t_info info;
t_edge *edge;
} ;
/**/ /*数据类型:记录最佳选区划分方案*/
typedef struct t_ans ... {
int s, win;
int set[7][MaxN];
t_info set_info[7];
} ;
t_node node[MaxN]; /**/ /*每个街区信息*/
int num_node; /**/ /*街区数目*/
bool visited[MaxN]; /**/ /*记录一个街区是否已经划分到某个选区*/
bool set[ 7 ][MaxN]; /**/ /*记录每个选区包括的街区,set[i][j]为true表示街区j划分在选区i内*/
int num_set[ 7 ]; /**/ /*记录每个选区内街区数目*/
t_info set_info[ 7 ]; /**/ /*保存每个选区人数和支持票数目*/
int connected[ 7 ][MaxN]; /**/ /*记录某个街区当前是否与选区相邻*/
t_ans best_ans; /**/ /*保存最优方案*/
int tot; /**/ /*记录当前找到的选区划分方案数目*/
int SPECIAL; /**/ /*Mevo本人就所在街区*/
/**/ /*添加街区u和街区v相邻的信息*/
void insert_edge( int u, int v) ... {
t_edge *p = new t_edge;
p->v = v, p->next = node[u].edge;
node[u].edge = p;
p = new t_edge;
p->v = u, p->next = node[v].edge;
node[v].edge = p;
}
/**/ /*
从info.txt文件读入街区信息
info.txt中信息格式:
第一行是街区数目num_node
接下来num_node行每行“街区编号id 街区人数num 街区中支持票数sup”
街区相邻信息个数num_edge
接下来num_edge行每行为一条街区相邻信息”街区编号i 街区编号j“表示街区i与街区j相邻(i<j)
Mevo所在选区
*/
void get_info() ... {
freopen("info.txt", "r", stdin);
int num_edge, id;
scanf("%d", &num_node);
for (int i = 1; i <= num_node; ++ i) ...{
scanf("%d", &id);
scanf("%d %d", &node[id].info.num, &node[id].info.sup);
}
/**//*初始化为每个街区都不与其它街区相邻*/
for (int ix = 1; ix <= num_node; ++ ix) ...{
node[ix].edge = NULL;
}
scanf("%d", &num_edge);
int u, v;
for (int i = 1; i <= num_edge; ++ i) ...{
scanf("%d %d", &u, &v);
insert_edge(u, v);
}
scanf("%d", &SPECIAL);
}
/**/ /*
将街区no加入到选区s中,
更新街区no为已经划入选区
更新选区信息:
选区街区数加一,
增加选区s中街区no的人数和支持票数目,
将所有与no相邻的街区设为可以划入选区s
*/
void add_set( int s, int no) ... {
visited[no] = true;
++ num_set[s];
set_info[s].num += node[no].info.num;
set_info[s].sup += node[no].info.sup;
set[s][no] = true;
t_edge *p = node[no].edge;
while (p != NULL) ...{
++ connected[s][p->v];
p = p->next;
}
}
/**/ /*
将街区no从选区s中剔除,
更新街区no为未划入选区
更新选区信息:
选区街区数减一,
减掉选区s中街区no的人数和支持票数目,
将所有与no相邻的街区设为不可以划入选区s
*/
void set_del( int s, int no) ... {
-- num_set[s];
visited[no] = false;
set_info[s].num -= node[no].info.num;
set_info[s].sup -= node[no].info.sup;
set[s][no] = false;
t_edge *p = node[no].edge;
while (p != NULL) ...{
-- connected[s][p->v];
p = p->next;
}
}
/**/ /*
将当前方案设为最佳方案
*/
void copy_ans( int s, int win) ... {
best_ans.s = s;
best_ans.win = win;
for (int ix = 1; ix <= s; ++ ix) ...{
for (int i = 1; i <= num_node; ++ i) ...{
best_ans.set[ix][i] = set[ix][i];
}
best_ans.set_info[ix].num = set_info[ix].num;
best_ans.set_info[ix].sup = set_info[ix].sup;
}
}
/**/ /*
输出当前方案
合格方案数加一
*/
void print_possible( int s, int win) ... {
++ tot;
printf("--------------------- ");
printf("合格方案 %d: ", tot);
for (int ix = 1; ix <= s; ++ ix) ...{
printf("选区%d: ", ix);
for (int i = 1; i <= num_node; ++ i) ...{
if (set[ix][i]) ...{
printf("街区%d ", i);
}
}
printf(" ");
printf("在本选区共%d张选票中获得%d张选票", set_info[ix].num, set_info[ix].sup);
if (set_info[ix].num < set_info[ix].sup*2) ...{
printf(" 胜 ");
} else ...{
printf(" 败 ");
}
}
printf("在%d个选区中获得%d个席位. ", s, win);
}
/**/ /*
检验当前方案:
如果当前选区划分不合法(选区数目不为5或6)则返回
如果当前方案合格,则输出
并检查是否比当前最佳方案更优,是则更新当前方案为最优方案
*/
bool check_ans( int s) ... {
if (! (s == 5 || s == 6) ) ...{
return false;
}
int win = 0;
for (int ix = 1; ix <= s; ++ ix) ...{
if (set_info[ix].num < set_info[ix].sup*2) ...{
++ win;
}
}
print_possible(s, win); /**//*输出当前方案*/
/**//*如果当前方案为胜出方案且比原来最佳方案划分选区数目少,
或当前方案比原来赢得的席位多,
则当前方案比原来的最优方案更优,将当前方案设置为最优方案*/
if ((s < best_ans.s && win*2 > num_set[s]) || win > best_ans.win )...{
copy_ans(s, win);
}
return true;
}
/**/ /*
递归实现划分过程:
将街区no加入选区s
如果选区s是合格的选区,则将其设为一个选区,划分下一个选区;
或:
在当前选区中添加与当前选区中街区相邻的街区
递归终止条件为
所有街区都已经属于一个选区,为一种可行方案,进行检验
选区数目大于6个,不合法,返回
优化:
依照街区编号顺次划分选区,选区标号大的选区的最下街区编号必定大于标号大的选区的最小街区编号
*/
void DFS( int no, int s, int count) ... {
add_set(s, no); /**//*将街区no加入选区s*/
if (count == num_node) ...{ /**//*如果所有街区都已经属于一个选区*/
check_ans(s);
goto END;
}
if (s > 6) return ; /**//*选区数目大于6个,不合法,返回*/
int t;
/**//*如果当前选区s是合格的选区,假设他为一个选区,划分下一个选区*/
if (s < 6 && ( (set_info[s].num >= MIN_POP && num_set[s] > 1) ||
num_set[s] == 1 && set_info[s].num >= MIN_SINGLE && no != SPECIAL)) ...{
/**//*找到一个编号最小的为加入选区的街区划分入下一个选区*/
t = 1;
while (visited[t]) ...{
++ t;
}
DFS(t, s+1, count+1);
}
/**//*或
在当前选区基础上继续添加街区,
优化:保证新添加街区必定与原来选区中街区相邻,加入的街区不会使选区人数超过上限
*/
for (int ix = 1; ix <= num_node; ++ ix) ...{
if (connected[s][ix] && !visited[ix]) ...{
if (node[ix].info.num + set_info[s].num <= MAX_POP) ...{
DFS(ix, s, count+1);
}
}
}
END:
set_del(s, no); /**//*把街区no从选区s中剔除*/
}
/**/ /*
初始化,令所有选区为空,所有街区为为划入任何选区
并调用DFS(1,1,1)开始求解
*/
void solve() ... {
memset(num_set, 0, sizeof(num_set));
memset(connected, 0, sizeof(connected));
for (int ix = 1; ix <= 6; ++ ix) ...{
set_info[ix].num = set_info[ix].sup = 0;
}
best_ans.win = 0, best_ans.s = 6;
DFS(1, 1, 1);
}
/**/ /*输出最佳方案*/
void print_ans() ... {
printf("============================== ");
printf("最佳方案: ");
for (int ix = 1; ix <= best_ans.s; ++ ix) ...{
printf("选区%d: ", ix);
for (int i = 1; i <= num_node; ++ i) ...{
if (best_ans.set[ix][i]) ...{
printf("街区%d ", i);
}
}
printf(" ");
printf("在本选区共%d张选票中获得%d张选票 ", best_ans.set_info[ix].num, best_ans.set_info[ix].sup);
if (best_ans.set_info[ix].num < best_ans.set_info[ix].sup*2) ...{
printf(" 胜 ");
} else ...{
printf(" 败 ");
}
}
printf("在%d个选区中获得%d个席位. ", best_ans.s, best_ans.win);
//system("PAUSE");
}
int main()
... {
freopen("AllSolution.txt", "w", stdout);
get_info();
solve();
print_ans();
return 0;
}