题目描述:戳这里
题解:SB状压题啊。。。
这题注意到题目中有一个条件,每一个点最多可以经过两次。这样就不能愉快地直接状压了。但是我们可以考虑一下三进制的状压DP,每个点可以经过1次或者两次。
那么直接两点之间有边就转移就可以了。
然而这题卡内存,开大一点就MLE。。。
我改了半天,又WA了。。。
最后才发现原来是-1的情况的判断没有注意到ans的大小。。。
无语。。。
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int maxn=20,maxm=177250;
int n,m,tot,ans;
int lnk[maxn],son[maxn*maxn],nxt[maxn*maxn],w[maxn*maxn],f[maxn][maxm],c[maxn];
int inv[maxn];
void clr(){
tot=0; ans=1<<30;
memset(lnk,0,sizeof(lnk));
memset(nxt,0,sizeof(nxt));
memset(son,0,sizeof(son));
memset(f,63,sizeof(f));
memset(w,0,sizeof(w));
}
void add(int x,int y,int z){
son[++tot]=y,w[tot]=z,nxt[tot]=lnk[x],lnk[x]=tot;
}
bool make_(int x){
memset(c,0,sizeof(c));
for (int i=0;i<n;i++) c[i]=x%3,x/=3;
bool check=0;
for (int i=0;i<n;i++) if (!c[i]) {check=1; break;}
return (!check);
}
int make_back(int x){
int ret=0;
for (int i=0;i<n;i++) ret+=inv[i]*c[i];
return f[x][ret];
}
int main(){
inv[0]=1;
for (int i=1;i<=12;i++) inv[i]=inv[i-1]*3;
while (~scanf("%d %d",&n,&m)) {
clr();
for (int i=1;i<=m;i++) {
int x,y,z; scanf("%d %d %d",&x,&y,&z);
add(x,y,z); add(y,x,z);
}
for (int i=0;i<n;i++) f[i+1][inv[i]]=0;
for (int j=1;j<inv[n];j++)
for (int i=1;i<=n;i++) {
bool check=make_(j);
if (!c[i-1]) continue;
if (f[i][j]==0) continue;
c[i-1]--;
for (int k=lnk[i];k;k=nxt[k])
if (c[son[k]-1]) f[i][j]=min(f[i][j],make_back(son[k])+w[k]);
if (check) ans=min(ans,f[i][j]);
}
if (ans>1000000000) printf("-1\n"); else printf("%d\n",ans);
}
return 0;
}