玄学状压?

/*
宝藏
1.20%数据为一颗树,而且边权相等
设x为根,那么代价显然是:
∑d[x]*v;
d[x]表示x节点到根节点的距离
树的重心问题!
我们考虑树形DP
先求出了所有节点的大小size[x]
其实我们只需要找到最大子树大小最小的节点即可!
dfs(int u,int fa)
{
size[u]=1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa)
continue;
dfs(v,u);
maxs[u]=max(maxs[u],size[v]);
size[u]+=size[v];
}
maxs[u]=max(maxs[u],n-size[u]);
if(maxs[u]<maxs[w])
w=u;
}
接着就进行求值!从w开始跑一边dfs
dfs2(int u,int fa)
{
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa)
continue;
ans+=val*size[v];
dfs2(v,u);
}
}
ans;

我们考虑用一个12位的2进制数表示当前的状态
设S表示当前所有的联通点集
对于(1<<x-1)&S=0
的点x
有S位种连边方法
我们用邻接矩阵维护一下图的连通性
dis[x][y]
d[S]表示实现状态S的最小代价:
for(int k=1;k<=n;k++)
{
memset(d,0x3f,sizeof(d));
d[1<<k-1]=0;//考虑枚举每一个可能的点!
for(int i=(1<<k-1);i<=(1<<n)-1;i++)
for(int j=1;j<=n;j++)
{
if(S&(1<<j-1)!=0)
continue;
d[S|(1<<j-1)]=min(d[S|(1<<j-1)],d[S]+work(S,j));
//其中work函数用来求S集合中的点到j的min距离!
}
ans=min(ans,d[1<<n-1]);
}
printf(ans);
*/
#include<cstdio>
#include<cstring>
#define INF 214748
using namespace std;
const int N=1200;
int dis[13][13];
int d[6000],num[13];
int n,m;
inline int max(int x,int y)
{
return x>y?x:y;
}
inline int min(int x,int y)
{
return x<y?x:y;
}
int ans;
int lowbit(int x)
{
return x & (-x);
}
int work(int s,int x)
{
int t=0,res=INF,k=0;
while(s)
{
t=lowbit(s);
s-=t;
if(dis[t][x]!=INF&&dis[t][x]*num[t]<res)
{
num[x]=num[t]+1;
res=dis[t][x]*num[t];
}
}
return res;
}
int main()
{
int x,y,z;
ans=INF;
for(int i=1;i<=12;i++)
for(int j=1;j<=12;j++)
dis[i][j]=INF;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
dis[x][y]=dis[y][x]=z;
}
for(int k=1;k<=n;k++)
{
memset(d,0x3f,sizeof(d));
for(int t=1;t<=12;t++)
num[t]=INF;
d[1<<k-1]=0;
num[k]=1;
for(int S=(1<<k-1);S<=(1<<n)-1;S++)
{
for(int j=1;j<=n;j++)
{
if(S&(1<<j-1))
continue;
d[S|(1<<j-1)]=min(d[S|(1<<j-1)],d[S]+work(S,j));
}
}
ans=min(ans,d[(1<<n)-1]);
}
printf("%d",ans);
return 0;
}

posted @ 2019-11-14 21:37  会飞的字符串  阅读(127)  评论(0编辑  收藏  举报