欢迎光临散文网 会员登陆 & 注册

牛客竞赛题目讲解_宝藏(noip)

2022-05-17 17:04 作者:Clayton_Zhou  | 我要投稿

// https://ac.nowcoder.com/acm/problem/16418

#include "stdafx.h"

//#include <bits/stdc++.h>

#include <algorithm>

#include <iostream>

 

using namespace std;

const int maxn=12;

int d[maxn+5][maxn+5];

int f[maxn+5][(1<<maxn)+5];

int dis[(1<<maxn)+5][(1<<maxn)+5];

int lg[(1<<maxn)+5];//预处理,求对数

int q[(1<<maxn)+5],cnt;


int edge[32][3]={

1, 2, 1,

1, 3, 3,

1 ,4, 1,

2, 3, 4,

3, 4, 2,

3,5,1,

5,6,5

};

int main()

{

memset(f,1,sizeof f);

//ios::sync_with_stdio(0);

//cin.tie(0),cout.tie(0);

int n,m;

n=6,m=7;

//cin>>n>>m;

for(int i=0;i<n;++i)

lg[1<<i]=i;//预处理,求对数

for(int i=0;i<12;++i)

for(int j=0;j<12;++j)

d[i][j]=1000000;//赋最大值

for(int i=1;i<=m;++i)

{

int a,b,c;

//cin>>a>>b>>c;

a=edge[i-1][0];

b=edge[i-1][1];

c=edge[i-1][2];

--a,--b,d[a][b]=d[b][a]=min(d[a][b],c); 

}

/*

T={1,2,3,4,5}

21=  10101  表示子集  {1,3,5}

  */

int  S=(1<<n)-1;//全集定义 

for(int i=1;i<=S;++i)

{

cnt=0;

for(int j=S^i;j;j=(j-1)&(S^i)) q[++cnt]=j;//由于这样做子集的顺序是从大到小的,不符合DP的顺序,所以要逆序 

for(int j=cnt;j>=1;--j)

{

int u=lg[q[j]&-q[j]],e=1000000;

for(int v=0;v<n;++v)

if(1<<v&i) e=min(d[u][v],e);//  集合i包含顶点v

dis[i][q[j]]=dis[i][q[j]^(q[j]&-q[j])]+e;//  集合i到其补集合的子集的距离

}

}

for(int i=0;i<n;++i) f[1][1<<i]=0;//初始状态

for(int i=2;i<=n;++i)

for(int j=(1<<i)-1;j<=S;++j) 

{

/*

新开发一条道路的代价是:   L × K  


L  代表这条道路的长度,K  代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的 宝藏屋的数量

(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。

*/

for(int k=j;k;k=(k-1)&j)// 遍历所有 集合j的子 集合

{// j^k   与  k 合起来就是集合j

f[i][j]=min(f[i][j],f[i-1][j^k]+dis[j^k][k]*(i-1));

if(i==5 && j==S&& f[i-1][j^k]==13)

{

cout<<"j="<<j<<", k="<<k<<", j^k="<<(j^k);

cout<<",,,f[i][j]="<<f[i][j]<<", f[i-1][j^k]="<<f[i-1][j^k]<<", dis[j^k][k]="<<dis[j^k][k]<<endl;

}

}

}

int ans=(1<<30);

for(int i=1;i<=n;++i) 

{

ans=min(ans,f[i][S]);//取最小值

cout<<",,"<<f[i][S]<<endl;

}

cout<<ans<<endl;

return 0;

}


牛客竞赛题目讲解_宝藏(noip)的评论 (共 条)

分享到微博请遵守国家法律