最优配对问题 :
枚举集合s‘中任意两点i,j使得s’集合中的状态最优,并将s'中的最优状态转移给s集合。
#include<bits/stdc++.h>
#include<iomanip>
using namespace std;
const int inf=1e9;
struct node{
double x,y;
}p[25];
double dp[1<<25];
double dis(node a,node b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main(){
int n;int flag=1;
while(cin>>n&&n){
n*=2;
for(int i=0;i<n;i++){
string s;cin>>s;
cin>>p[i].x>>p[i].y;
}
// cout<<"asd"<<endl;
dp[0]=0;
for(int i=1;i<(1<<n);i++)
dp[i]=inf;
for(int s=1;s<(1<<n);s++){
for(int i=0;i<n;i++){
if(s&(1<<i)){
for(int j=i+1;j<n;j++){
if(s&(1<<j))
dp[s]=min(dp[s],dis(p[i],p[j])+dp[s^(1<<i)^(1<<j)]);//这里去掉i和j后的集合一定是比s小的集合,也就是比s小的数,那一定是已经遍历过的集合了
}
}
}
}
cout<<"Case "<<flag++<<": ";
cout<<fixed<<setprecision(2);
cout<<dp[(1<<n)-1]<<endl;
}
}
货郎担问题(TSP):
写法一:从集合中取出两个点
for(int s=1;s<(1<<n);s++){
for(int i=0;i<n;i++){
if(s&(1<<i)){
for(int j=0;j<n;j++){//j也要从0开始,因为从i到j和从j到i是不同的,因为如果i在集合中和j在集合中他们连接的之前的点可能不同,那么继承的路程也可能不同
if(s&(1<<j)&&mp[j][j]){
dp[i][s]=min(dp[i][s],dp[j][s^(1<<j)]+mp[j][i]);//dp(i,s)代表当前走到的城市是i,已经经过的城市是s集合那他一定是从s中的任意一点j转移而来的,即dp[j][s']
}
}
}
}
}
写法二:向集合中加点
for(int s=1;s<(1<<n);s++){
for(int i=0;i<n;i++){
if(s&1(1<<i)){
for(int j=0;j<n;j++){
if(!s&(1<<n)&&mp[i][j]){
dp[i][s|(1<<i)]=min(dp[i][s|(1<<i)],dp[i][s]+mp[i][j]);
}
}
}
}
}
例题:hdu-5418