我的第一届华中地区数学建模联赛B题程序

8 篇文章 0 订阅
       哈哈,我这个对数模一窍不通的家伙被拉去参加了数学建模,体验了一把数模跟ACM/ICPC的不同和联系。
 
    更加奇异的是,我第一次参加数模做数模题就拿了一等,呵呵——主要赞一下我们队长的文笔啦……
    想当初我拿到题目之后看到B题就当ACM的题目做啦,结果队友不明白,还好我最后把自己的思想给他们讲清楚了,然后(哈哈)通过队长的妙笔,我们就拿奖了。
 
   在这里放出我当时写的程序吧——写得比较水,仅供笑料,呼呼
   ps:这个题目属于NP问题,我们参加ACM的一般就搜索加剪枝了,无奈我比较水,写得非常暴力,大家将就吧。
 
题目:
 

在—个遥远的国家,Sark Mevo 所领导的政党最终击败了Reguel Tekris王子领导的联合党派。Mevo希望巩固他在首都地区的席位。首都由14个街区组成,这些街区将分组为多个选区。下图是首都地区的示意图。在图中用数字114对这些街区进行了编号。每个街区中的另外两个数字是预计该街区会投票给Mevo的选民数和该街区的选民总数。所有选民都必须投票,且选举胜出方必须得到绝对多数选票。一个选区可以由多个相邻的街区组成,且选区内总选民数应在30,000100,000之间。如果两个街区不相邻,例如1213,则它们不能组成一个选区。如果某个街区选民人数不少于50,000,则允许此街区单独作为一个选区。但是由于Mevo本人就居住在街区10内,因此迫于舆论压力,他不能将这个街区单独作为一个选区。

    请设计出一个将首都划分为5个选区的方案,以使Mevo得到的席位数最多。如果这样做有困难,可以尝试划分为6个选区。

 

 

 

 

 

 

我写的程序(加了不少的注释,呵呵):

 

 

 

 

 

 

 

 

/*
author:littlekid
本程序在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 
*= new t_edge;
    p
->= v, p->next = node[u].edge;
    node[u].edge 
= p;
    
    p 
= new t_edge;
    p
->= 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 
*= 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 
*= 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 > 6return ; /*选区数目大于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(
111);
}


/*输出最佳方案*/
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;
}

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
第四届文鼎创杯华中地区大学生数学建模a涉及到了一个复杂的实际问,需要参赛选手们深入分析和解决。该目可能涉及到某个具体的工程或者经济管理领域的实际问,需要选手们熟练掌握数学建模的方法和技巧,同时具备良好的团队合作能力。 在面对这样的挑战时,选手们需要首先对问进行仔细的分析和思考,理清思路,确定建模的方向和方法。接着需要充分利用数学知识,如微积分、概率统计、线性代数等,将实际问转化为数学模型,寻找问的规律和特点。然后,选手们需要借助计算机软件或者编程语言,对模型进行仿真和求解,得出可行的解决方案。最后,选手们需要对模型的准确性和鲁棒性进行充分的验证和评估,确保所提出的方案能够在实际应用中得到有效的解决。 在解答这样的数学建模目时,选手们需要充分发挥团队合作的优势,相互协作,共同攻克问,实现优势互补,充分发挥每个人的专长和才能。同时,也需要充分调动自己的积极性和创造性,敢于挑战和创新,在思维上敢于突破传统,寻找独特的解决方案。 总之,第四届文鼎创杯华中地区大学生数学建模a对于参赛选手来说是一个难度较大的挑战,需要他们不断学习和积累,善于总结经验和方法,不断加强团队协作,提升解决问的能力和水平。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值