最小總代價 狀壓DP
阿新 • • 發佈:2019-01-11
題目描述
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;
}