题目描述
n个人在做传递物品的游戏,编号为1-n。
游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;
求当物品经过所有n个人后,整个过程的总代价是多少。
数据范围
(2<=n<=16)
样例输入
2
-1 9794
2724 –1
样例输出
2724
解题思路
这个数据范围很容易让人想到爆搜或者是状压什么的。但是如果是爆搜,我只会n!的做法,所以应该不是爆搜,于是就开始写状压。之后十分天真地写出了一个错误的状压。
↓↓↓这就是我原来的错误代码↓↓↓
#include <bits/stdc++.h>
using namespace std;
inline int Getint(){int x=0,f=1;char ch=getchar();while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
int main(){
int Map[17][17],f[65536];
int n=Getint();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
Map[i][j]=Getint();
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)
f[1<<(i-1)]=0;
for(int i=0;i<(1<<n);i++){
for(int j=1;j<=n;j++){
if(i&(1<<(j-1)))
continue;
for(int k=1;k<=n;k++)
if(i&(1<<(k-1)))
f[i+(1<<(j-1))]=min(f[i+(1<<(j-1))],f[i]+Map[k][j]);
}
}
cout<<f[(1<<n)-1];
return 0;
}
很显然,这一份代码根本就没有记录当前状态是来自何处,于是求出来的答案并不满足条件。但是,我当我用这个错误的程序过了样例之后就果断交上了我们学校的OJ。。。而且,,,居然有85分。。。。
之后就写了一个n阶乘暴力,然后对拍,发现n=3都会很容易错,调了一下,发现了问题,之后订正后再交,就AC了。。其实最后还被<<这个的优先级坑了一下。。
(其实上面的都是废话,QAQ)
f[i][j]表示什么怎么转移就直接看代码吧。。。
代码
#include <bits/stdc++.h>
using namespace std;
inline int Getint(){int x=0,f=1;char ch=getchar();while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
int Map[17][17],f[65536][17];
int main(){
int n=Getint();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
Map[i][j]=Getint();
for(int i=0;i<(1<<n);i++)
for(int j=1;j<=n;j++)
f[i][j]=1<<30;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
f[1<<(i-1)][j]=1<<30;
f[1<<(i-1)][i]=0;
}
for(int i=1;i<(1<<n);i++)
for(int j=1;j<=n;j++){
if(i&(1<<(j-1)))continue;
for(int k=1;k<=n;k++)
if(f[i][k]!=1<<30)
f[i|1<<(j-1)][j]=min(f[i|1<<(j-1)][j],f[i][k]+Map[k][j]);
}
int Min=1<<30;
for(int i=1;i<=n;i++)
Min=min(Min,f[(1<<n)-1][i]);
cout<<Min;
return 0;
}