0-1背包问题(回溯法)
题目描述
有n个物品,第i个物品重量为wi,价值为vi,现有一背包容量为C,要求把物品装入背包得到最大价值,并且要求出这些选取的物品。 要求用回溯法求解。
输入
多组测试数据,请处理到文件尾,一个整数表示物品的数量n,后一行有n个整数,代表价值,再后一行有n个整数,代表重量,最后有一个整数C代表背包容量,1<=n<=15,1<=vi<=30,1<=wi<=30,1<=C<=80。
输出
背包的最大总价值和所选取的物品,如果选取的方案有多种,请输出字典序最小的那种方案,每组测试数据应输出一行,在这里字典序最小的意思是,我们假设存在两种不同方案S,T所能得到的总价值相同且是最大的,对于方案S种选取|S|种物品,方案T选取|T|种物品,对于i=1,2...j-1,我们有si = ti,但sj < tj,则方案的S的字典序比方案T的字典序要小。由于没有使用special judge,所以如果选取得方案是S,请按照从小到大的顺序输出方案S中的物品下标。
样例输入 Copy
5
6 3 6 5 4
2 2 4 6 5
8
样例输出 Copy
15 1 2 3
我不信注释都这么详细了还能够看不懂!!!!!!
#include<iostream>
#include<math.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<stdio.h>
using namespace std;
int n,C;
int w[20],v[20];//背包中各个数的重量和价值
int best_ans[20],ans[20];//最优解和当前解 ,判断背包中各个数是否被装入
int wsum,vsum,mvsum;//当前已选背包中的重量和价值、最大价值
void dfs(int t,int ww,int vv){//这里传入当前的背包剩余容量和价值,是为了维护当前这个价值
if(t>n){//大于n时表示已经找到了一组解
if(vsum>mvsum){
mvsum=vsum;
for(int i=1;i<=n;i++)best_ans[i]=ans[i];//替换最优值的选取方法
}
else if(vsum==mvsum){
for(int i=1;i<=n;i++){
if(best_ans[i]>ans[i])//如果字典序比当前的字典序大,则保留字典序小的
{
for(int j=1;j<=n;j++)
best_ans[i]=ans[i];
break;
}
}
}
}
else{//否则继续判断
int temp=vsum;//这里是一个剪枝,也是我认为比较精妙的地方
for(int i=t;i<=n;i++)temp+=v[i];//这个是为了判断到了当前这个地方的时候直接把后面的所有价值相加与最优价值相比较,如果比最优价值小那么这一组解就没有必要继续回溯下去了
if(temp<mvsum)return;
if(wsum>=w[t]){//如果还能够装入下一个物品
wsum-=w[t];
vsum+=v[t];
ans[t]=1;//标记已经放入
dfs(t+1,wsum,vsum);//继续搜索下一个
ans[t]=0;//切记回溯后得回归原来的位置
wsum+=w[t];
vsum-=v[t];
}
dfs(t+1,wsum,vsum);
}
}
int main(){
while(cin>>n){
memset(ans,0,20);
memset(best_ans,0,20);//每次都要初始化
for(int i=1;i<=n;i++)cin>>v[i];//价值
for(int i=1;i<=n;i++)cin>>w[i];//重量
cin>>C;//总容量
wsum=C,vsum=0,mvsum=0;
dfs(1,wsum,vsum);
cout<<mvsum;
for(int i=1;i<=n;i++){
if(best_ans[i]==1)cout<<" "<<i;
}
cout<<endl;
}
return 0;
}
N皇后问题
题目描述
使用回溯法求解N后问题。
输入
皇后的个数。
输出
每一种方案及总方案数。
样例输入 Copy
4
样例输出 Copy
0 1 0 0 0 0 0 2 3 0 0 0 0 0 4 0 ---------------- 0 0 1 0 2 0 0 0 0 0 0 3 0 4 0 0 ---------------- 总方案数为:2
本题详解可以看我这一篇文章
#include<bits/stdc++.h>
using namespace std;
int sum=0;
int mm[1005][1005];
int x[1005];//x[i]表示第i行在x[i]列
int n;
int isright(int t){
for(int i=1;i<t;i++){
if((abs(t-i)==abs(x[t]-x[i]))||x[t]==x[i])return 0;//判断是否满足条件
}
return 1;
}
void queen(int k){
if(k>n){//行数为k,当k大于n时有解了
sum++;//方案数加1
for(int i=1;i<=n;i++){//把每一行的皇后的位置赋值给mm数组中的同一行列值
mm[i][x[i]]=i;
}
for(int i=1;i<=n;i++)//输出数组
for(int j=1;j<=n;j++){
if(j!=n)cout<<mm[i][j]<<" ";
else cout<<mm[i][j]<<endl;
}
memset(mm,0,sizeof(mm));//一定要记得清空数组,因为如果有多组解那么他会造成上一组的解仍然保留
//cout<<endl;
cout<<"----------------"<<endl;
}
else
for (int i = 1; i <= n; i++)
{
x[k] = i;
if (isright(k))//如果当前行满足条件则判断下一行
queen(k + 1);//回溯
}
}
int main(){
cin>>n;
queen(1);//从第一行开始判断
cout<<"总方案数为:"<<sum<<endl;
return 0;
}