[ L i n k \frak{Link} Link]
注意:洛谷题解里面有很大一部分题解是错误的
具体可以用讨论中的hack数据自行验证。
点数很特殊。
最开始的想法是类似最小生成树那样的东西,不过这个“最小”定义实在有点模糊。
注意到数据范围。n的范围十分特殊,考虑一下。
按理来说不是搜索就是状态压缩了,而且状压的可能性更大一点。
不过还是先考虑搜索吧。
首先枚举起点,然后逐个跑最小生成树?
要注意的是,边权不是固定的,和之前的方案有关。所以最小生成树是跑不了了。
不过还是可以骗一点分:尝试枚举全排列,同时每一步还要枚举新的点是哪个点拓展出来的。
#include
#include
#include
#include
using namespace std;
int n,m,dis[15][15]={},dep[15]={};
int pt[15][15]={};
int mn[15]={};
long long ans=0x3f3f3f3f;
bool vis[15]={};
int stk[15]={};
inline void dfs(register int step,register long long sum)
{
if(sum>=ans) return;
if(step==n) { ans=min(ans,sum); return;}
for(register int i,ti=1;ti<=stk[0];++ti)
{
i=stk[ti];
for(register int v,j=1;j<=pt[i][0];++j)
{
v=pt[i][j];
if(vis[v])continue;
vis[v]=1, dep[v]=dep[i]+1; stk[++stk[0]]=v;
dfs(step+1,sum+1ll*dis[i][v]*dep[v]);
vis[v]=0; --stk[0];
}
}
}
int main()
{
memset(mn,0x3f,sizeof(mn));
memset(dis,0x3f,sizeof(dis));
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;++i)dis[i][i]=0;
for(register int u,v,w,i=1;i<=m;++i)
{
scanf("%d%d%d",&u,&v,&w);
if(dis[u][v]==0x3f3f3f3f)
{
pt[u][++pt[u][0]]=v;
pt[v][++pt[v][0]]=u;
}
if(w<dis[u][v])
{
dis[u][v]=dis[v][u]=w;
mn[u]=min(mn[u],w);
mn[v]=min(mn[v],w);
}
}
stk[0]=1;
for(register int i=1;i<=n;++i) { vis[i]=1; dep[i]=0; stk[1]=i; dfs(1,0); vis[i]=0;}
printf("%d",ans);
return 0;
}
没有特意剪枝,剪枝AC的代码可以参考洛谷题解。
最小生成树跑不了,所以在Prim贪心的基础上随机接受非最短边。
具体见洛谷题解
状压的根本在于按(被压缩的)层转移。
初步考虑 f ( S ) \frak{f(S)} f(S)表示 S \frak{S} S里面的点都被选入,此时的最小代价。
转移的影响因素有两个,一个是转移边的权值,一个是转移点的层数
第二个是动态变化的。于是状态变为 f ( i , S ) \frak{f(i,S)} f(i,S)。
所以枚举 i \frak{i} i和 S \frak{S} S,然后枚举 S ′ \frak{S'} S′表示要放在 i + 1 \frak{i+1} i+1层的点集。
复杂度是 Θ ( n ⋅ 4 n ) \frak{\Theta(n·4^n)} Θ(n⋅4n)。实际上里面的剪枝剪掉了很多重复的状态,仔细考虑一下?
实际上因为 S ′ \frak{S'} S′是 S \frak{S} S补集的子集也就是全集的子集的子集,
枚举 S \frak{S} S的一层复杂度可以无视掉,复杂度实际上就是枚举全集的子集的子集的复杂度。
考虑一个元素数量为 n \frak{n} n的集合,它的元素个数为 x \frak{x} x的子集数量为 C n x \frak{C_n^x} Cnx,
且元素个数为 x \frak{x} x的集合的所有子集数量为 2 x \frak{2^x} 2x。
综上所述,元素个数为 n \frak{n} n的集合的子集的子集数量为 ∑ x = 1 n C n x 2 x \frak{\sum\limits_{x=1}^{n}C_n^x2^x} x=1∑nCnx2x。
这个形式应该挺熟悉的,就是二项式定理那个 ∑ r = 1 n C n r a n − r b r \frak{\sum\limits_{r=1}^nC_n^ra^{n-r}b^r} r=1∑nCnran−rbr取 a = 2 , b = 1 \frak{a=2,b=1} a=2,b=1。
所以数量就是 ( 2 + 1 ) n = 3 n \frak{(2+1)^n}=3^n (2+1)n=3n
综上所述复杂度为 Θ ( n ⋅ 3 n ) \frak{\Theta(n·3^n)} Θ(n⋅3n)。
#include
#include
#include
#include
#include
#include
using namespace std;
int n,m,Ans=0x3f3f3f3f;
int F[15][5000]={};
int dis[15][15]={};
int cost[15][5000]={};
int to[5000][5000]={};
void out(int x)
{
while(x)
{
if(x&1)putchar('1');
else putchar('0');
x>>=1;
}cout<<" ";
}
int main()
{
memset(dis,0x3f,sizeof(dis));
memset(cost,0x3f,sizeof(cost));
scanf("%d%d",&n,&m);
for(register int u,v,w,i=1;i<=m;++i)
{
scanf("%d%d%d",&u,&v,&w);
if(w<dis[u][v])dis[u][v]=dis[v][u]=w;
}
for(int i=1;i<=n;++i)
{
for(int j=0;j<(1<<n);++j)
{
if(j&(1<<i-1))continue;
for(int k=1;k<=n;++k)
{
if(j&(1<<k-1))cost[i][j]=min(cost[i][j],dis[i][k]);
}
}
}
for(int i=0;i<(1<<n);++i)
{
for(int j=0;j<(1<<n);++j)
{
if(i&j)continue;
for(int k=1;k<=n;++k)
{
if(i&(1<<k-1))to[i][j]=min(to[i][j]+cost[k][j],0x3f3f3f3f);
}
}
}
memset(F,0x3f,sizeof(F));
for(int i=1;i<=n;++i)F[1][1<<i-1]=0;
for(register int f=1;f<n;++f)
{
for(register int j=0;j<(1<<n);++j)
{
if(F[f][j]==0x3f3f3f3f)continue;
for(register int k=0;k<(1<<n);++k)
{
if(j&k)continue;
if(to[k][j]==0x3f3f3f3f)continue;
F[f+1][j|k]=min(F[f+1][j|k],F[f][j]+f*to[k][j]);
}
}
}
for(register int i=1;i<=n;++i)Ans=min(Ans,F[i][(1<<n)-1]);
if(Ans==0x3f3f3f3f)printf("-1");
else printf("%d",Ans);
return 0;
}
其他做法:
n 3 3 n \frak{n^33^n} n33n、 2 n n m \frak{2^nnm} 2nnm、 3 n n \frak{3^nn} 3nn