赞
踩
【编译环境】devc++ 5.11
【解题思路】
tsp问题:
1.输入图的结点个数和代价矩阵
2.按顺序生成集合的子集
3.动态规划法依次处理子集
4.对于复杂性
【参考内容】
d[i][j]表示从顶点i经过子集v[j]中的顶点一次且仅一次,最后回到出发点0的路径长度。
【代码分析】
头文件
#include <stdio.h>
#include<malloc.h>
#include<stdlib.h>
生成集合的函数
long unsigned int* collect(int max)//max表示元素最大值。 { //sum表示子集数量 int sum; //index指向遍历位置 int index = 0; //indexin指向填表位置 int indexin = 0; //n用来存储中间量 unsigned int n=0; //组合结果总个数,不包含全空的情况,故减一 sum = (int)pow(2,max)-1; long unsigned int* col = (long unsigned int*)malloc(sizeof(int)*sum); //初始化为二进制1,10,100,1000,10000... //即集合{1,2}表示为011,集合{3}表示为100,通过固定位上是否为1表示集合中是否含有这个数 for(int i=0;i<max;i++) { col[indexin++] = (unsigned int)pow(2,i); } while(index<=sum) { for(int i=max-1;i>=0;i--)//从高位判断,集合的最高位方便生成下一位 { n = (unsigned int)pow(2,i); //判断固定位上是否位1 if((col[index]&n) != 0 )//!=的优先级大于&运算符的优先级所以要加括号 { if(i==max-1) { break; } for(int j=i+1;j<max;j++)//j<max表示最高生成的集合元素位max { //利用加法表示集合001+010相当于集合{1,2} col[indexin++] = col[index]+(unsigned int)pow(2,j); } break; } } //生成完毕,开始下一次循环 index++; } //方便展示生成的集合 /* for(int i=0;i<sum;i++)//二进制格式输出 { char s[max+1]; itoa(col[i],s,2); printf("%s\n",s); } */ return col; }
展示生成的集合,形式如下
节点个数为5时:
一些解释
因为生成集合的函数使用位数表示集合元素是否存在,所以很方便通过与和非操作去除一个顶点
vindex[j]&(~(long unsigned int)pow(2,h-1))
假设vindex[j]为二进制1111即集合{1,2,3,4}
当h为2时,则pow(2,h-1):10为二进制10即集合{2}
非:1101
再进行与操作:1111与1101->1101即从原集合中去除了元素2。
头文件和宏定义
#include<time.h>
#include<random>
#define MAX 999//设置最大权值
#define MAXNODE 5//节点数
#define INF 99999//默认最大值
数据初始化
int main() { int node; //自动生成方式 //数据写入文件 FILE* fp = fopen("demo.TXT","w"); //生成随机数 srand((unsigned)time(NULL)); node = MAXNODE; //生成数组矩阵,用一维数组表示二维数组 int *d = (int*)malloc(sizeof(int)*999*999); for(int i=0;i<node;i++) { for(int j=0;j<node;j++) { //行列相等默认最大值 if(j == i) d[i*node+j] = INF; else d[i*node+j] = rand()%MAX+1; fprintf(fp, "%d\t",d[i*node+j]); fprintf(fp, " "); } fprintf(fp, "\n"); } //关闭文件 fclose(fp); //调用tsp函数 tsp(d,node); }
算法实现按照参考图片中第二步
//d表示数组矩阵。n表示节点个数(包括0节点) void tsp(int* d,int n) { //数组的索引序号,表示数组处理的顺序 long unsigned int*vindex; //数组下标代表不同子集,数组存储相应节点对应的最短路径长度 int *v = (int*)malloc(sizeof(int)*n*(int)pow(2,n)); //记录向量解 int vec[n*(int)pow(2,n)+1]={0}; //初始化 for(int i=0;i<n;i++) { v[i*(int)pow(2,n)+0] = d[i*n+0]; for(int j=1;j<(int)pow(2,n);j++) { v[i*(int)pow(2,n)+j] = INF; } } //vindex存储的就是遍历子集的顺序,n-1表示子集元素最多为n-1个,0作为出发点 vindex = collect(n-1); //以下算法每一步都是解释算法书上的伪代码 int j=0; while(j<pow(2,n-1)-1)//依次处理每一个子集数组v[j] { if(j==pow(2,n-1)-2)//此时是全集的情况,输出最短路径 { long unsigned int min=INF; int m=0; for(int h=1;h<n;h++)//遍历全集中每一个元素 { if(min>d[0*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]) { min=d[0*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]; vec[0*((int)pow(2,n)+1)+vindex[j]]=h; } } v[0*(int)pow(2,n)+vindex[j]]=min; break;//跳出循环直接输出 } //开始处理每一个子集 for(int i=1;i<n;i++) { if((vindex[j]&(int)pow(2,i-1))==0)//v[j]中不包含i { long unsigned int min=INF; //对Vindex[j]中每一个元素h,计算v[i][j]=min{d[i][h]+v[h][j-1]} for(int h=1;h<n;h++)//遍历每一个元素,计算最小路径 { if((vindex[j]&(long unsigned int)pow(2,h-1))!=0)//判断元素h在vindex[j]中 { if(min>d[i*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]) { min=d[i*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]; vec[i*((int)pow(2,n)+1)+vindex[j]]=h; } } } v[i*(int)pow(2,n)+vindex[j]]=min; } } j++; } //打印表格,向量解,最短路径长度 printout(d,v,vindex,vec,n); }
void printout(int*d,int*v,unsigned long int*vindex,int*vec,int n) { //输出表格 printf("\t%-8d",0); for(int i=0;i<pow(2,n-1)-1;i++)//二进制格式输出 { char s[n]; itoa(vindex[i],s,2); printf("%-8s",s); } printf("\n"); for(int i=0;i<n;i++) { printf("%-8d",i); if(d[i*n+0]<INF) printf("%-8d",d[i*n+0]); else printf("%-8d",0); for(int j=0;j<pow(2,n-1)-1;j++) { if(v[i*(int)pow(2,n)+vindex[j]]>=INF) printf("%-8d",0); else printf("%-8d",v[i*(int)pow(2,n)+vindex[j]]); } printf("\n"); } //输出向量解 //记录每一层循环里最小的值作为下一个节点 int maxcomb=pow(2,n-1)-2;//减去空集减去全集减去编号从0开始造成的偏移,所以减3 int last; int nextlast = vec[0*((int)pow(2,n)+1)+vindex[(int)pow(2,n-1)-2]]; int vecindex= vindex[(int)pow(2,n-1)-2];//全集的第一个向量 unsigned long int min; printf("向量解:<%d->%d> ",0,nextlast); for(int j=n;j>1;j--)//寻找n次向量解,每个阶段一次 { if(j==2) { printf("<%d->%d> \n",nextlast,0); break; } last = nextlast; vecindex = (vecindex&(~(long unsigned int)pow(2,last-1)));//集合中删除last nextlast = vec[last*((int)pow(2,n)+1)+vecindex]; printf("<%d->%d> ",last,nextlast); } //输出最短路径长度 printf("最短路径:%ld",v[vindex[(int)pow(2,n-1)-2]]); }
#include <stdio.h> #include<malloc.h> #include<time.h> #include<random> #include<stdlib.h> //宏定义区 #define MAX 99//设置最大权值 #define MAXNODE 5//节点数 #define INF 9999//默认最大值 //全局变量区 //函数声明区 void tsp(int*,int); long unsigned int* collect(int); void printout(int*,int*,unsigned long int*,int*,int);//数组矩阵,子集值,子集索引,子集向量解,最大元素 int main() { int node; //自动生成方式 //数据写入文件 FILE* fp = fopen("demo.TXT","w"); //生成随机数 srand((unsigned)time(NULL)); node = MAXNODE; //生成数组矩阵,用一维数组表示二维数组 int *d = (int*)malloc(sizeof(int)*999*999); for(int i=0;i<node;i++) { for(int j=0;j<node;j++) { //行列相等默认最大值 if(j == i) d[i*node+j] = INF; else d[i*node+j] = rand()%MAX+1; fprintf(fp, "%d\t",d[i*node+j]); fprintf(fp, " "); } fprintf(fp, "\n"); } //关闭文件 fclose(fp); //调用tsp函数 tsp(d,node); } long unsigned int* collect(int max)//max表示元素最大值。 { //sum表示子集数量 int sum; //index指向遍历位置 int index = 0; //indexin指向填表位置 int indexin = 0; //n用来存储中间量 unsigned int n=0; //组合结果总个数,不包含全空的情况,故减一 sum = (int)pow(2,max)-1; long unsigned int* col = (long unsigned int*)malloc(sizeof(int)*sum); //初始化为二进制1,10,100,1000,10000... //即集合{1,2}表示为011,集合{3}表示为100,通过固定位上是否为1表示集合中是否含有这个数 for(int i=0;i<max;i++) { col[indexin++] = (unsigned int)pow(2,i); } while(index<=sum) { for(int i=max-1;i>=0;i--)//从高位判断,集合的最高位方便生成下一位 { n = (unsigned int)pow(2,i); //判断固定位上是否位1 if((col[index]&n) != 0 )//!=的优先级大于&运算符的优先级所以要加括号 { if(i==max-1) { break; } for(int j=i+1;j<max;j++)//j<max表示最高生成的集合元素位max { //利用加法表示集合001+010相当于集合{1,2} col[indexin++] = col[index]+(unsigned int)pow(2,j); } break; } } //生成完毕,开始下一次循环 index++; } //方便展示生成的集合 /* for(int i=0;i<sum;i++)//二进制格式输出 { char s[max+1]; itoa(col[i],s,2); printf("%s\n",s); } */ return col; } void printout(int*d,int*v,unsigned long int*vindex,int*vec,int n) { //输出表格 printf("\t%-8d",0); for(int i=0;i<pow(2,n-1)-1;i++)//二进制格式输出 { char s[n]; itoa(vindex[i],s,2); printf("%-8s",s); } printf("\n"); for(int i=0;i<n;i++) { printf("%-8d",i); if(d[i*n+0]<INF) printf("%-8d",d[i*n+0]); else printf("%-8d",0); for(int j=0;j<pow(2,n-1)-1;j++) { if(v[i*(int)pow(2,n)+vindex[j]]>=INF) printf("%-8d",0); else printf("%-8d",v[i*(int)pow(2,n)+vindex[j]]); } printf("\n"); } //输出向量解 //记录每一层循环里最小的值作为下一个节点 int maxcomb=pow(2,n-1)-2;//减去空集减去全集减去编号从0开始造成的偏移,所以减3 int last; int nextlast = vec[0*((int)pow(2,n)+1)+vindex[(int)pow(2,n-1)-2]]; int vecindex= vindex[(int)pow(2,n-1)-2];//全集的第一个向量 unsigned long int min; printf("向量解:<%d->%d> ",0,nextlast); for(int j=n;j>1;j--)//寻找n次向量解,每个阶段一次 { if(j==2) { printf("<%d->%d> \n",nextlast,0); break; } last = nextlast; vecindex = (vecindex&(~(long unsigned int)pow(2,last-1)));//集合中删除last nextlast = vec[last*((int)pow(2,n)+1)+vecindex]; printf("<%d->%d> ",last,nextlast); } //输出最短路径长度 printf("最短路径:%ld",v[vindex[(int)pow(2,n-1)-2]]); } //d表示数组矩阵。n表示节点个数(包括0节点) void tsp(int* d,int n) { //数组的索引序号,表示数组处理的顺序 long unsigned int*vindex; //数组下标代表不同子集,数组存储相应节点对应的最短路径长度 int *v = (int*)malloc(sizeof(int)*n*(int)pow(2,n)); //记录向量解 int vec[n*(int)pow(2,n)+1]={0}; //初始化 for(int i=0;i<n;i++) { v[i*(int)pow(2,n)+0] = d[i*n+0]; for(int j=1;j<(int)pow(2,n);j++) { v[i*(int)pow(2,n)+j] = INF; } } //vindex存储的就是遍历子集的顺序,n-1表示子集元素最多为n-1个,0作为出发点 vindex = collect(n-1); //以下算法每一步都是解释算法书上的伪代码,vec数组用于后面方便输出 int j=0; while(j<pow(2,n-1)-1)//依次处理每一个子集数组v[j] { if(j==pow(2,n-1)-2)//此时是全集的情况,输出最短路径 { long unsigned int min=INF; int m=0; for(int h=1;h<n;h++)//遍历全集中每一个元素 { if(min>d[0*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]) { min=d[0*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]; vec[0*((int)pow(2,n)+1)+vindex[j]]=h; } } v[0*(int)pow(2,n)+vindex[j]]=min; break;//跳出循环直接输出 } //开始处理每一个子集 for(int i=1;i<n;i++) { if((vindex[j]&(int)pow(2,i-1))==0)//v[j]中不包含i { long unsigned int min=INF; //对Vindex[j]中每一个元素h,计算v[i][j]=min{d[i][h]+v[h][j-1]} for(int h=1;h<n;h++)//遍历每一个元素,计算最小路径 { if((vindex[j]&(long unsigned int)pow(2,h-1))!=0)//判断元素h在vindex[j]中 { if(min>d[i*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]) { min=d[i*n+h]+v[h*(int)pow(2,n)+(vindex[j]&(~(long unsigned int)pow(2,h-1)))]; vec[i*((int)pow(2,n)+1)+vindex[j]]=h; } } } v[i*(int)pow(2,n)+vindex[j]]=min; } } j++; } //打印表格,向量解,最短路径长度 printout(d,v,vindex,vec,n); }
【解题思路】
1.获取背包容量packvol,物品数量n,物品价值和重量value[],weigth[]
2.动态规划法求解01背包问题
3.对于复杂性
【参考内容】
背包最大价值
装入背包的物品
【代码分析】
//价值矩阵
int v[n+1][packvol+1]={0};
//背包是否装入物品
int x[n+1];
//初始化第0行和第0列
for(int i=0;i<=packvol;i++)
{
v[0][i]=0;
}
for(int i=0;i<=n;i++)
{
v[i][0]=0;
x[i]=0;
}
//依次遍历数组
for(int i=1;i<=n;i++)//物品
{
for(int j=1;j<=packvol;j++)//背包容量
{
if(weight[i-1]>j)//装不下
v[i][j]=v[i-1][j];
if(weight[i-1]<=j)//装的下,继续判断是否最优
{
v[i][j] = v[i-1][j-weight[i-1]]+value[i-1]>v[i-1][j]?v[i-1][j-weight[i-1]]+value[i-1]:v[i-1][j];
}
}
}
//输出表格
for(int i=0;i<=n;i++)
{
for(int j=0;j<=packvol;j++)
{
printf("%-4d",v[i][j]);//左对齐输出
}
printf("\n");
}
//向量解
printf("向量解{行,列}:");
for(int i=n,j=packvol;i>0;i--)
{
printf("{%d,%d}->",i,j);
if(v[i][j]>v[i-1][j])
{
x[i]=1;//参考内容第三个图片
j= j-weight[i-1];
}
if(i==1)
{
printf("{%d,%d}",i-1,j);
}
}
//最优解
printf("\n最优解:");
for(int i=1;i<=n;i++)
{
printf("%d",x[i]);
}
printf("\n最大价值:%d",v[n][packvol]);
#include<stdio.h> #include<time.h> #include<random> //宏定义区 #define MAX 60//背包最大容量 #define MAXN 30//最大物品数 #define MAXW 7//物品最大重量 #define MAXV 7//物品最大价值 //全局变量区 //自定义函数区 void randomint(int,int,int*,int*); void fwritein(int,int,int*,int*); void Pack(int,int,int*,int*); int main() { int n,packvol; srand(time(NULL)); n = rand()%MAXN; packvol = rand()%MAX+1; int w[n],v[n]; randomint(packvol,n,w,v); Pack(n,packvol,w,v); return 0; } void fwritein(int packvol,int n,int* weight,int* value) { FILE* fp = fopen("demo2-2.TXT","w"); fprintf(fp,"背包容量:%d\n",packvol); fprintf(fp,"物品数量:%d\n",n); for(int i=0;i<n;i++) { fprintf(fp,"%d\t%d\t%d\n",i,weight[i],value[i]); } fclose(fp); } void randomint(int packvol,int n,int* weight,int* value) { for(int i=0;i<n;i++) { weight[i]=rand()%MAXW+1; value[i]=rand()%MAXV+1; } fwritein(packvol,n,weight,value); return; } void Pack(int n,int packvol,int* weight,int* value) { int v[n+1][packvol+1]={0}; int x[n+1]; for(int i=0;i<=packvol;i++) { v[0][i]=0; } for(int i=0;i<=n;i++) { v[i][0]=0; x[i]=0; } for(int i=1;i<=n;i++) { for(int j=1;j<=packvol;j++) { if(weight[i-1]>j) v[i][j]=v[i-1][j]; if(weight[i-1]<=j) { v[i][j] = v[i-1][j-weight[i-1]]+value[i-1]>v[i-1][j]?v[i-1][j-weight[i-1]]+value[i-1]:v[i-1][j]; } } } for(int i=0;i<=n;i++) { for(int j=0;j<=packvol;j++) { printf("%-4d",v[i][j]); } printf("\n"); } printf("向量解{行,列}:"); for(int i=n,j=packvol;i>0;i--) { printf("{%d,%d}->",i,j); if(v[i][j]>v[i-1][j]) { x[i]=1; j= j-weight[i-1]; } if(i==1) { printf("{%d,%d}",i-1,j); } } printf("\n最优解:"); for(int i=1;i<=n;i++) { printf("%d",x[i]); } printf("\n最大价值:%d",v[n][packvol]); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。