joannae
NPC问题及其解决方法(回溯法、动态规划、贪心法、深度优先遍历)

NP问题(Non-deterministic Polynomial ):多项式复杂程度的非确定性问题,这些问题无法根据公式直接地计算出来。比如,找大质数的问题(有没有一个公式,你一套公式,就可以一步步推算出来,下一个质数应该是多少呢?这样的公式是没有的);再比如,大的合数分解质因数的问题(有没有一个公式,把合数代进去,就直接可以算出,它的因子各自是多少?也没有这样的公式)。

NPC问题(Non-deterministic Polynomial complete):NP完全问题,可以这么认为,这种问题只有把解域里面的所有可能都穷举了之后才能得出答案,这样的问题是NP里面最难,但是这样算法的复杂程度,是指数关系。一般说来,如果要证明一个问题是NPC问题的话,可以拿已经是NPC问题的一个问题经过多项式时间的变化变成所需要证明的问题,那么所要证明的问题就是一个NPC问题了。NPC问题是一个问题族,如果里面任意一个问题有了多项式的解,即找到一个算法,那么所有的问题都可以有多项式的解。

著名的NPC问题:

背包问题(Knapsack problem):01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2……Wn,与之相对应的价值为V1,V2……Vn。求出获得最大价值的方案。

旅行商问题(Traveling Saleman Problem,TSP),该问题是在寻求单一旅行者由起点出发,通过所有给定的需求点之后,最后再回到原点的最小路径成本。

哈密顿路径问题(Hamiltonian path problem)与哈密顿环路问题(Hamiltonian cycle problem)为旅行推销员问题的特殊案例。哈密顿图:由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次。

欧拉回路(从图的某一个顶点出发,图中每条边走且仅走一次,最后回到出发点;如果这样的回路存在,则称之为欧拉回路。)与欧拉路径(从图的某一个顶点出发,图中每条边走且仅走一次,最后到达某一个点;如果这样的路径存在,则称之为欧拉路径。)

无向图欧拉回路存在条件:所有顶点的度数均为偶数。
无向图欧拉路径存在条件:至多有两个顶点的度数为奇数,其他顶点的度数均为偶数。
有向图欧拉回路存在条件:所有顶点的入度和出度相等。
有向图欧拉路径存在条件:至多有两个顶点的入度和出度绝对值差1(若有两个这样的顶点,则必须其中一个出度大于入度,另一个入度大于出度),其他顶点的入度与出度相等。

01背包问题解决方法

法I:回溯法递归

复制代码
public:
void Knapsack(int w,int v, int c,int n){//w:容量;v:value
this->c = c;
this->n = n;
bestv = 0;
bool x[n] = {false}; //x: 是否选择这个物品
backtracking(w,v,x,0);
}

void backtracking(int depth, int w, int v, bool *x){
if(depth >= n){
if(tmpV > bestv){
bestv = tmpV;
for(int i = 0; i < n; i++){
bestx[i] = x[i];
}
}
return;
}
if(tmpW + w[depth] <= c){ //加入当前元素
x[i] = true;
tmpW += w[depth];
tmpV += v[depth];
backtracking(depth+1, w, v, x);
tmpV -= v[depth]; //backtrack
tmpW -= w[depth];
x[i] = false;
}

backtracking(depth+1, w, v, x);//不加入当前元素

}

private:
int bestv; //最优方法的价值
int* bestx; //最优方法选取的物品
int tmpV; //已有价值
int tmpW; //已使用的容量
int c; //背包容量
int n; //物品数量
复制代码

法II:动态规划

1。定义阶段:v[i-1]表示第i个物品的价值
2。定义状态:V[n+1][C]前i个物品装入容量为j的背包中获得的最大价值
3。状态转移方程:V[i][j]=max(V[i-1][j],V[i-1][j-w[i-1]]+v[i-1]);
4。定义边界条件:V[i][0]=0;V[0][j]=0;

复制代码
int KnapSack(int n,int w[],int v[],int x[],int C){
int V[n+1][C];//前i个物品装入容量为j的背包中获得的最大价值
int i,j;
for(i=0;i<=n;i++)
V[i][0]=0;
for(j=0;j<=C;j++)
V[0][j]=0;
for(i=1;i<=n-1;i++)
for(j=1;j<=C;j++)
if(j < w[j]) V[i][j]=V[i-1][j];
else V[i][j]=max(V[i-1][j],V[i-1][j-w[i-1]]+v[i-1]);

 //标示哪些物品被放入j=C;for(i=n;i>0;i--)if(V[i][j]>V[i-1][j]){x[i-1]=1;j=j-w[i-1];}else x[i-1]=0;return V[n][C];

}
复制代码

法III: 贪心法解决普通背包问题

普通背包问题:与0-1背包问题类似,所不同的是在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包,1≤i≤n。

贪心准则:每一项计算yi=vi/wi,再按比值的降序来排序,从第一项开始装背包,然后是第二项,依次类推,尽可能的多放,直到装满背包。适用于普通背包问题,但不适用于01背包问题。

旅行商问题解决方法

法I:回溯法递归

复制代码
int bestd;
vector< int > bestv;//保存最优解路径上的节点
int tmpSum;
vector< int > tmpV; //暂时保存路径上的节点
unordered_set visited;

void shortest( int **d,int n){
bestd = INT_MAX;
dfs(d,n,0);
}

void dfs (int **d,int n, int depth){
tmpV.push_back(depth);
if(tmpV.size()==n){
tmpSum += d[depth][0];

    if(tmpSum < bestd){bestd = tmpSum;bestv = tmpV;}tmpSum -= d[depth][0]; //backtracktmpV.pop();return;
}
visited.insert(depth);
for(int i = 0; i < n; i ++){if(visited.find(i) != visited.end() && tmpSum + d[depth][i] < bestd){tmpSum += d[depth][i];dfs(d,n,i);tmpSum -= d[depth][i]; //backtrack}
}
visited.erase(depth); //backtrack
tmpV.pop_back();

}
复制代码

法II:动态规划

1。定义阶段:v[i-1]表示第i个物品的价值
2。定义状态:F[i][j]表示当前从i结点出发已访问j中节点的情况下的最短距离。其中,i表示当前访问的节点,i∈[0,n-1];j=已访问的节点的bitmap,j∈[0,2^(n-1)-1]
3。状态转移方程:F[i][j] = min{ min,D[i][k] + F[k][j-(int)pow(2,k-1)]) }
4。定义边界条件:F[i][0] = D[i][0]即表示节点i到第一个节点的距离,D是原图的邻接矩阵

复制代码
void tsp(int** D, int n){
int i,j,k,min,temp;
int b=(int)pow(2,n-1); //已遍历的节点bitmap(除了最后一个节点,每个节点有选择及不选择两种情况)

//申请二维数组F和M
int ** F = new int* [n];//n行b列的二维数组,存放阶段最优值
int ** M = new int* [n];//n行b列的二维数组,存放最优策略
for(i=0;i < n; i++){F[i] = new int[b];M[i] = new int[b];
}//初始化F[][]和M[][]
for(i=0;i < n; i++)for(j=0;j < n; j++){F[j][i] = -1;M[j][i] = -1;}
for(i=0;i < n; i++) F[i][0] = D[i][0];//状态转移
for(i=1;i < b; i++)for(j=1;j < n; j++){if( ((int)pow(2,j-1) & i) == 0){//结点j不在i表示的集合中min=INT_MAX;for(k=1;k < n; k++ ){ //从已访问过的节点中找出一个到节点j的距离最短if( (int)pow(2,k-1) & i ){//非零表示结点k在集合中temp = D[j][k] + F[k][i-(int)pow(2,k-1)];//去掉k结点即将k对应的二进制位置0if(temp < min){min = temp;F[j][i] = min;//保存阶段最优值M[j][i] = k;//保存最优决策}}}}
//最后一列,即总最优值的计算
min=INT_MAX;
for(k=1;k < n; k++ ){//b-1的二进制全1,表示全集temp = D[0][k] + F[k][b-1 - (int)pow(2,k-1)]; //去掉kif(temp < min){min = temp;F[0][b-1] = min;M[0][b-1] = k;}
}
cout<<"最短路径长度:"<<F[0][b-1]<<endl;//最短路径长度
cout<<"最短路径(编号0—n-1):"<<"0"; //最短路径上的节点
for(i=b-1,j=0; i>0; ){//i的二进制是5个1,表示集合{1,2,3,4,5}j = M[j][i];//下一步去往哪个结点i = i - (int)pow(2,j-1);//从i中去掉j结点cout<<"->"<<j;
}
cout<<"->0"<<endl;

}
复制代码

法III: 启发式贪心法

采用启发式贪心算法。对于那些受大自然的运行规律或者面向具体问题的经验、规则启发出来的方法,人们常常称之为启发式算法(Heuristic Algorithm)。启发式算法得到的解只是近似最优解。步骤:

(1)从旅行商问题的n个城市中选择1个城市构成部分解序列T1={c1},共有n种初始组合。

(2)从部分解序列之外的城市中选择一个新的城市k,插到原有的部分解序列Tk-1={c1,c2,…,ck-1}中,得到新的部分解列Tk={c1,c2,…,ck,…,ck-1}。新的城市ck及插入位置由改进的贪心法确定。

用Tk-1={c1, c2,…,ck-1}表示已确定的部分解序列,则由min(d(ci,ck)+d(ck,ci+1) -d(ci,ci+1)),ci,ci+1∈Tk-1,ck∈NP完全问题确定插入的城市ck及插入位置(ci,ck,ci+1)

(3)用冒泡法对新的部分解序列Tk中的每个城市进行可能优化游路的换位、移位和倒位操作,直到不再能通过这些操作优化游路。

对于旅行商问题的一个解序列,可以通过换位、移位和倒位三种基本的次序变换操作,改变原来解序列的排列次序,得到新的解序列。其它游路改进的启发式操作,都可以由这三种基本操作组合而成。

换 位操作(exchange):将解序列中第i个元素ci与第j个元素cj的位置交换。ΔD换位=(d(ci- 1,ci)+d(ci,ci+1)+d(cj-1,cj)+d(cj,cj+1))-(d(ci-1,cj)+d(cj,ci+1)+d(cj-1,ci)+d(ci,cj+1))

移 位操作move :移位操作相当于选择(Or2opt)操作,它将解序列中第i个元素ci移动到第j个元素cj之后的位置上。ΔD移位=(d(ci- 1,ci)+d(ci,ci+1)+d(cj,cj+1))-(d(ci-1,ci+1)+d(cj,ci)+d(ci,cj+1))

倒位操作(inverse) :倒位操作相当于选择操作取r=2的情况,它将解序列中从第i个元素ci到第j个元素cj之间的元素的顺序前后颠倒。倒位操作的性能指标为:

ΔD倒位=(d(ci-1,ci)+d(cj,cj+1))-(d(ci-1,cj)+d(ci,cj+1))

(4)如果部分解序列的长度k

欧拉路径求解方法

法I:Fleury算法(深度优先遍历)

数据结构:栈

复制代码
int stk[1005];
int top;
int N, M, ss, tt;
int mp[1005][1005];

void dfs(int x) { //深度优先遍历
stk[top++] = x;
for (int i = 1; i <= N; ++i) {
if (mp[x][i]) {
mp[x][i] = mp[i][x] = 0; // 删除此边
dfs(i);
break;
}
}
}

void fleury(int start) {
bool brige;
top = 0; //top永远指向下一个要入栈元素的存放位置
stk[top++] = start; // 将起点放入Euler路径中
while (top > 0) {
brige = true; //割边(桥,最后一条连通外界的边)也已经遍历了
for (int i = 1; i <= N; ++i) { // 遍历节点
if (mp[stk[top-1]][i]) { //如果与栈顶节点有边
brige = false;
break;
}
}
if (brige) { // 如果没有点可以扩展,输出并出栈,下一个while循环的时候会搜索下一个栈顶元素的其他路径
printf("%d ", stk[--top]);
} else { // 否则继续搜索欧拉路径
dfs(stk[--top]);
} //从dfs返回,说明从节点stk[top-1]开始的深度遍历已结束,下面找与它连通的下一个节点(广度遍历)。
}
}

int main() {
int x, y, deg, num;
while (scanf("%d %d", &N, &M) != EOF) {
memset(mp, 0, sizeof (mp));
for (int i = 0; i < M; ++i) {
scanf("%d %d", &x, &y);
mp[x][y] = mp[y][x] = 1;
}
for (int i = 1; i <= N; ++i) { //计算节点度数,判断是否符合欧拉路径/欧拉回路的条件
deg = num = 0;
for (int j = 1; j <= N; ++j) {
deg += mp[i][j];
}
if (deg % 2 == 1) {
start = i, ++num; //设置起始点
printf("%d\n", i);
}
}
if (num == 0 || num == 2) {
fleury(start);
} else {
puts("No Euler path");
}
}
return 0;
}

转载于:https://blog.51cto.com/danlove/2049403

NPC问题及其解决方法相关推荐

  1. nagios npc安装后状态为off的解决方法

    1.检查ndo2db的进程是不是二个 nagios   16825  0.0  0.1   6784   396 ?        Ss   19:05   0:00 /usr/local/nagio ...

  2. 加载服务器版本信息,传奇服务器端启动加载错误的解决方法

    1.启动服务端M2报错的类型 2.错误分类,思路理清 3.文字总结以下常见现象 传奇服务器端启动加载错误解决方法 Exception] 物品数据库加载错误! [Exception] 魔法数据库加载错误 ...

  3. Unity3D占用内存太大的解决方法【先转,慢慢看】

    2019独角兽企业重金招聘Python工程师标准>>> Unity3D占用内存太大的解决方法 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大. 这里写下关于Unit ...

  4. unity3d占用内存太大解决方法

    原帖:http://www.onevcat.com/2012/11/memory-in-unity3d/ 转载自星辰 Unity3D占用内存太大的解决方法 最近网友通过网站搜索Unity3D在手机及其 ...

  5. GOM和GEE引擎黑屏不显示界面,装备地图怪物的解决方法

    GOM和GEE引擎黑屏不显示界面,装备地图怪物的解决方法 先简单的说一下服务端安装简单过程 今天艾西来总结一下,有哪些原因会出现黑屏的现象以及黑屏的解决方法! 1. 服务端放进D盘 2. 安装16周年 ...

  6. wow8.0服务器显示不兼容,?魔兽世界8.0系统血条无法显示原因是什么?解决方法又是什么?[图]...

    魔兽世界8.0版本自上线以来,出现了各种BUG,最近就有玩家反映系统血条无法显示.那么导致该问题的原因是什么呢?解决方法又是什么?想要对此了解的玩家就跟小编一起来看看吧. 魔兽世界8.0系统血条不显示 ...

  7. csgo卡住关不掉_csgo卡屏断开连接解决方法 | 手游网游页游攻略大全

    发布时间:2016-03-19 细心的玩家会发现失落城堡010最新补丁出现卡屏问题,小编给大家分享一下失落城堡010最新补丁出现卡屏问题解决方法.希望大家喜欢. 点击下载: 失落城堡010最新补丁出 ...

  8. 天谕登录显示服务器有问题,天谕手游无法登陆怎么办 天谕手游进不去解决方法...

    天谕手游无法登陆怎么办相信这个疑问很多玩家都有过,1月8日上午十点的正式开服,很多玩家都纷纷开始涌入这个游戏,但有些玩家发现游戏进不去?那么一起来和小编看看天谕手游进不去解决方法吧. 天谕手游进不去解 ...

  9. 传奇2私服XP下启动解决方法

    最简单的SF是"单机版"的,偶是指就在同一台机器上安服务器端和客户端,当然这个 就只能是你自己玩玩,看看传奇里一些你想知道的东西(当然要找一个仿SD的服务端) 还一种是在局域网内设 ...

  10. kali安装vscode和无法启动解决方法

    一.安装 1)源安装 使用如下命令来增加源: curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > m ...

最新文章

  1. 编程语言发展70年,用50种不同语言输出「Hello World」
  2. 华为交换机SSH登录失败原因
  3. 静态成员变量和非静态成员变量的5个主要区别
  4. c语言的标准字符,C语言标准定义的32个关键字
  5. 微软或在开发自己的 CPU、TikTok 发布电视版本、索尼撤下《赛博朋克2077》并为玩家退款|Decode the Week...
  6. weblogic中ssrf漏洞修复_Weblogic-SSRF漏洞复现
  7. 除了默认的docker0网桥,启动Docker服务怎么指定使用的网桥
  8. sqlserver 2008express版本启用混合登陆和sa
  9. kotlin协程_Kotlin协程
  10. 加载配置文件(xml文件,properties文件)demo
  11. 试喷一下今年的国家最高科学技术奖
  12. PS 仿制图章 轻松换支付宝赞赏码 GIF动态图手把手教你
  13. MySQL联合主键保存_mysql联合主键
  14. explain的用法
  15. 用Python给娃送上一份猪年春节礼物。文末源码!
  16. 欧姆龙PLC以太网与西门子WINCC通讯
  17. 弱电布线工程实战攻略
  18. 固定td宽度令其不随内容改变以及固定tr的高度
  19. 注意这些技巧,成为软文营销专家
  20. win10设备管理器没有android,win10设备管理器没有蓝牙设备怎么办-修复win10设备管理器没有蓝牙设备的方法 - 河东软件园...

热门文章

  1. SQL教程——TCL语言
  2. HMC5883L 磁力计校准
  3. 毕业找工作 送给大家一些漂亮的个人简历模板
  4. CDA Level1 考试心得
  5. 高速PCB设计怎么布局,资深工程师透露了其中的秘密,都是干货
  6. sfm支持Linux和Windows吗,VisualSFM的使用方法
  7. 阿里Maven仓库不限容量,免费用
  8. 代购集运系统平台一键上传淘宝商品至韩国coupang经验分享
  9. 电路课设-音响分频器电路设计
  10. 空洞卷积详解(输入输出大小分析)