6、批处理作业调度(排列树)
每一个作业Ji都有两项任务分别在2台机器上完成。每个作业必须先有机器1处理,然后再由机器2处理。作业Ji需要机器j的处理时间为tji。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理时间。则所有作业在机器2上完成处理时间和f=F2i,称为该作业调度的完成时间和。
也就是f1[i]=上一步的使用时间+自己消耗的时间,f2[i]=max(当前在机器1上的时间,上一步的使用时间)+自己消耗的时间。
详细的解释见参考文章
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int x[100];//当前的作业调度顺序
int bestx[100];//最优的调度顺序
int m[100][100];//各作业所需的处理时间
int f1,f2=0;//机器1、2完成时间
int bestf=1000;//最短完成时间
int cf=0,n;//当前完成时间和
Swap(int &a,int &b)
{
int t=a;
a=b;
b=t;
}
void BackTrace(int t)
{
if(t>n)
{
if(cf<bestf)
{
for(int i=1; i<=n; i++)
bestx[i]=x[i];
bestf=cf;
}
}
else
{
for(int i=t; i<=n; i++)
{
f1+=m[x[i]][1];
int tempf=f2;//保存的是上一个作业在f2的时间
f2=(f1>f2?f1:f2)+m[x[i]][2];
cf+=f2;
if(cf<bestf)//超过最优时间就没必要继续了
{
Swap(x[t],x[i]);//回到上一个位置
BackTrace(t+1);//再继续向下找
Swap(x[t],x[i]);//得到一个最优后就要回溯
}
f1-=m[x[i]][1];
cf-=f2;
f2=tempf;
}
}
}
int main()
{
int i,j;
cout<<"请输入作业数:"<<endl;
cin>>n;
cout<<"请输入在各机器上的处理时间"<<endl;
for(i=1; i<=2; i++) //i从1开始
for(j=1; j<=n; j++)
cin>>m[j][i];//第j个作业,第i台机器的时间值
for(i=1; i<=n; i++)
x[i]=i;//初始化当前作业调度的一种排列顺序
BackTrace(1);
cout<<"调度作业顺序:"<<endl;
for(i=1; i<=n; i++)
cout<<bestx[i]<<' ';
cout<<endl;
cout<<"处理时间:"<<endl;
cout<<bestf;
return 0;
}
/*
输入:
3
2 3 2
1 1 3
输出:
调度作业顺序:
1 3 2
处理时间:
18
*/
7、最大团问题
先看视频
理解后再看文章链接
总结: i从1开始向下找,如果i与当前所有解中有一个没有边,那么i不能放入解中,x[i]=0;否则cn++且x[i]=1。由于是深度优先搜索,所以先判断是否能进入左子树,能则(即x[i]=1)cn++,递归下一层且回退时cn–,不能则用限界函数判断是否需要进入右子树,cn+n-i>bestn时表示不选择当前的结点也可能获得更好的bestn,所以可以进入右子树。
#include <iostream>
using namespace std;
int m[100][100];//有向图的邻接矩阵
int x[100],bestx[100];//当前团的解 最优解
int n;//表示图的顶点数
int cn=0,bestn;//当前团的大小 当前最优值
void getbestn(int i)
{
if(i>n) //递归出口,到根节点时,更新最优值和最优解,返回
{
bestn=cn;//更新最优值
for(int j=1; j<=n; j++)
bestx[j]=x[j];
return ;//返回
}
x[i]=1;//先假定x[i]=1;
for(int j=1; j<i; j++)
{
if(x[j]==1&&m[i][j]==0)//如果该点与已知解中的点无边相邻
{
x[i]=0;
break;//则不遍历左子树
}
}
if(x[i]==1) //当且仅当x[i]==1时,遍历左子树
{
cn++;//该点加入当前解
getbestn(i+1);//递归调用
cn--;//还原当前解
}
if(cn+n-i>bestn)
//如果当前值+右子树可能选择的节点>当前最优解则可以遍历右子树,否则无需继续进行
{
x[i]=0;
getbestn(i+1);
}
return ;
}
int main()
{
cin>>n;//输入图的顶点数
//输入图的邻接矩阵
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
cin>>m[i][j];
//求最优解
getbestn(1);
cout<<bestn<<endl;//输出最优值
//输出
for(int i=1; i<=n; i++)
if(bestx[i])
cout<<i<<" ";//输出最优解
return 0;
}
/*
输入:
5
0 1 0 1 1
1 0 1 0 1
0 1 0 0 1
1 0 0 0 1
1 1 1 1 0
输出:
3
1 2 5
*/
8、图的m着色问题
先看视频链接
再看文章链接
总结: i从第一个节点开始,其颜色j从1到m种颜色中找,如果找到一个不会和相连结点重复的颜色,则color[j]++(第j种颜色被用过),同时递归下一层,没找到时回退同时color[j]–、i的颜色归0。到达叶节点时更新bestn、bestx[]。
代码:
#include<iostream>
using namespace std;
int c[100][100]; //邻接矩阵
int x[100],bestx[100]; //当前顶点i的颜色 最终节点i的颜色
int cn,bestn,m,n; //cn记录方案数 n个顶点 m种颜色
int color[100]; //color[i]!=0表示第i号颜色被用到过
int Check(int k) //检查第k个顶点的颜色是否满足条件
{
for(int i=1; i<=k; i++)
{
if(c[k][i]==1&&x[i]==x[k]) //k与i之间相连并且i顶点的颜色与k顶点的颜色相同
return 0;
}
return 1;
}
void GraphColor(int i)
{
if(i>n) //到达叶结点
{
cn=0;
for(int j=1; j<=m; j++)
{
if(color[j]) cn++;
}
if(bestn>cn||bestn==0)//出现更小的bestn
{
//更新最大颜色数
bestn=cn;
for(int j=1; j<=n; j++)
bestx[j]=x[j];
}
}
else
{
for(int j=1; j<=m; j++)
{
x[i]=j; //首先将这个顶点颜色换为i
if(Check(i)==1) //检查是否符合条件
{
color[j]++;//满足则颜色数++
GraphColor(i+1); //走下一步
color[j]--;
}
x[i]=0; //回溯 置为0
}
}
}
int main()
{
int a,b;
cin>>n>>m;
//邻接表
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
cin>>c[i][j];
//从第一个节点开始
GraphColor(1);
//输出各点的颜色
for(int j=1; j<=n; j++)
cout<<bestx[j]<<' ';
cout<<endl;
//总颜色数
cout<<bestn;
return 0;
}
/*
输入
5 4
0 1 1 1 0
1 0 1 1 1
1 1 0 1 0
1 1 1 0 1
0 1 0 1 0
输出
4 3 2 1 4
4
*/