版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/81878372
题目大意:
思路:
看样子数据范围这么小,可以搜索的样子,然后发现复杂度不行。
似乎一个一个点地加进去状态太多了,所以可以考虑一层一层地加点。
然后就成了一个三进制的DP,表示目前到了哪一层,这个点是否在里面以及是否是整个树的最后一层。
复杂度似乎还是太高,其实没有必要记录是否是整个树的最后一层,我门在加点集进去的时候,可以直接把每一个结点都当成是最后一层,然后直接加点集转移即可。
考虑这样的正确性,如果某一个点它加进去连的点实际上不是最后一层的点的话,好像会把答案算大的样子,其实没有关系,如果这个状态是最优状态的前状态,那么之前必定会把这个点添加到正确的位置并且计算的层数也是正确的。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;
using namespace std;
void File(){
freopen("loj2318.in","r",stdin);
freopen("loj2318.out","w",stdout);
}
const int maxn=12+10;
const int maxw=(1<<13)+10;
const int inf=0x3f3f3f3f;
int n,m,dp[maxw][maxn],all,cost[maxn][maxw],dis[maxn][maxn],ans;
void init(){
memset(dis,63,sizeof(dis));
scanf("%d%d",&n,&m);
all=(1<<n)-1;
int u,v,w;
REP(i,1,m){
scanf("%d%d%d",&u,&v,&w);
dis[u][v]=min(dis[u][v],w);
dis[v][u]=min(dis[v][u],w);
}
REP(i,1,n)REP(S,1,all){
if((1<<(i-1))&S)continue;
int Min=inf;
REP(j,1,n)if((1<<(j-1))&S)
Min=min(Min,dis[i][j]);
cost[i][S]=Min;
}
}
void out(int x,int len){
if(!len)return;
out(x/2,len-1);
cout<<x%2;
}
void work(){
memset(dp,63,sizeof(dp));
REP(i,1,n)dp[1<<(i-1)][1]=0;
REP(S,1,all)REP(dep,1,n){
if(dp[S][dep]==inf)continue;
//out(S,n);
//cout<<endl<<dep<<" "<<dp[S][dep]<<endl;
int S0=all^S,sum;
for(int S1=S0;S1;S1=(S1-1)&S0){
bool flag=true; sum=0;
REP(i,1,n)
if(((1<<(i-1))&S1) && cost[i][S]==inf)flag=false;
else if(((1<<(i-1))&S1) && flag)sum+=cost[i][S];
if(!flag)continue;
dp[S|S1][dep+1]=min(dp[S|S1][dep+1],dp[S][dep]+sum*dep);
}
}
ans=inf;
REP(i,1,n)ans=min(ans,dp[all][i]);
printf("%d\n",ans);
}
int main(){
File();
init();
work();
return 0;
}