/****************************************************
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;
}