一个编程题

/****************************************************

text.txt是一个文本文件,内含8行12列的96个正整数,其中行以’/n’分
割,列以’/t’分割。
试从这96 个数据中挑选出8 个数据,使得所挑选出的这8 个数据的和尽可能地小。
要求每行每列至多只能有一个数据被选中。

打印出这个最小和以及这8个数

解题思想1:要求每行每列至多只能有一个数据被选中,可以采用穷举遍历所有可能性的办法做
算法复杂度:P(n,m) = n(n-1)...(n-m+1) = n!/(n-m)! (这里n=12,m=8)

解题思路2:采用类似“八皇后问题”的搜索算法。

解题思路3:动态规划算法。采用一个二进制位长度为12的数标记前面的行用掉了哪些列,这就是一个状态


*******************************************************/

#include <stdio.h>
int a[8][12];
int resIndex[8];
int g_min;

void Solution1(){
 //采用了思路1,很暴力^_^
 int g_min = 2e31;
 for(int i1 = 0; i1 < 12; i1 ++)
  for(int i2 = 0; i2 < 12; i2 ++)
   for(int i3 = 0; i3 < 12; i3 ++)
    for(int i4 = 0; i4 < 12; i4 ++)
     for(int i5 = 0; i5 < 12; i5 ++)
      for(int i6 = 0; i6 < 12; i6 ++)
       for(int i7 = 0; i7 < 12; i7 ++)
        for(int i8 = 0; i8 < 12; i8 ++){
         if(i1 == i2 || i1 == i3 || i1 == i4 || i1 == i5 || i1 == i6 || i1 == i7 || i1 == i8)
          continue;
         if(i2 == i3 || i2 == i4 || i2 == i5 || i2 == i6 || i2 == i7 || i2 == i8)
          continue;
         if(i3 == i4 || i3 == i5 || i3 == i6 || i3 == i7 || i3 == i8)
          continue;
         if(i4 == i5 || i4 == i6 || i4 == i7 || i4 == i8)
          continue;
         if(i5 == i6 || i5 == i7 || i5 == i8)
          continue;
         if(i6 == i7 || i6 == i8)
          continue;
         if(i7 == i8)
          continue;
         int tmp = a[0][i1] + a[1][i2] + a[2][i3] + a[3][i4] + a[4][i5] + a[5][i6] + a[6][i7] + a[7][i8];
         if(tmp < g_min){
          g_min = tmp;
          resIndex[0] = i1; resIndex[1] = i2;
          resIndex[2] = i3; resIndex[3] = i4;
          resIndex[4] = i5; resIndex[5] = i6;
          resIndex[6] = i7; resIndex[7] = i8;
         }
        }
 printf("by solution 1: %d/n",g_min);
 for(int i = 0; i < 8; i ++){
  printf("%d/t",a[i][resIndex[i]]);
 }
 printf("/n");
 printf("the index:/n");
 for(int i = 0; i < 8; i ++){
  printf("%d/t",resIndex[i]);
 }
 printf("/n");
}

bool visited[12];
int tmpIndex[12];

void Try(int i){
//从第i列开始遍历每一种可能性
 for(int j = 0; j < 12; j ++){
  if(visited[j] == false){
   tmpIndex[i] = j;
   visited[j] = true;
   if(i < 7)
    Try(i + 1);
   else{
    int sum = 0;
    for(int k = 0; k < 8; k ++){
     sum += a[k][tmpIndex[k]];
    }
    if(sum < g_min){
     g_min = sum;
     for(int k = 0; k < 8; k ++)
      resIndex[k] = tmpIndex[k];
    }
   }
   //把状态置回,以便于回溯
   visited[j] = false;
  }
 }
}

void Solution2(){
//采用思路2,是代码易懂,思路较简单的一种方法,建议掌握
 for(int i = 0; i < 12; i ++)
  visited[i] = false;
 g_min = 2e31;
 Try(0);
 printf("by solution 2: %d/n",g_min);
 for(int i = 0; i < 8; i ++){
  printf("%d/t",a[i][resIndex[i]]);
 }
 printf("/n");
 printf("the index:/n");
 for(int i = 0; i < 8; i ++){
  printf("%d/t",resIndex[i]);
 }
 printf("/n");
}

void Solution3(){
//采用思路3,这里只给出了这个最小值的大小,而那8个数分别是哪几个,需要再添些代码,有兴趣下去思考一下吧。
 int best[9][4096];
 for(int i = 0; i < 9; i ++)
  for(int j = 0; j < 4096; j ++)
   best[i][j] = 2e31;
 for(int i = 0; i < 12; i ++){
  best[0][1 << i] = a[0][i];
 }
 for(int i = 1; i < 8; i ++){
  for(int j = 0; j < 12; j ++){
   for(int k = 0; k < 4096; k ++){
     if ( ! ((1 << j) & k) && best[i][k | (1 << j)] > best[i - 1][k] + a[i][j]){
      // !(1<<j)&k 用来判断k标记的状态中第j列用过没有,如果用过(1<<j)&k为1,取非,结果为0
      best[i][k | (1<<j)] = best[i - 1][k] + a[i][j];
     }
   }
  }
 }
 int tmp = 2e31;
 int bestI = -1;
 for(int i = 0; i < 4096; i ++){
  if(best[7][i] < tmp){
   tmp = best[7][i];
   bestI = i;
  }
 }
 printf("the min: %d/n",tmp);
 printf("the bestI: %d/n",bestI);
 //int tmpK = bestI;
 //int index = 0;
 //for(int i = 0; i < 12; i ++){
 // if(tmpK % 2 == 1){
 //  restIndex[index ++] = i;
 //  tmpK /= 2;
 // }
 //}
}


int main(){
 //从text.txt读入数据存入a[i][j]
 freopen("text.txt","r",stdin);
 for(int i = 0; i < 8; i ++)
  for(int j = 0; j < 12; j ++)
   scanf("%d",&a[i][j]);
 Solution1();
 Solution2();
 Solution3();
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值