title
LUOGU 3959
JYOJ 1465
题目描述
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度。
小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。
小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。
在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。
新开发一条道路的代价是:
L代表这条道路的长度,K代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的 宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。
请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代 价最小,并输出这个最小值。
输入输出格式
输入格式:
第一行两个用空格分离的正整数 n,m,代表宝藏屋的个数和道路数。
接下来 m 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏 屋的编号(编号为 1-n),和这条道路的长度 v。
输出格式:
一个正整数,表示最小的总代价。
输入输出样例
输入样例#1:
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1
输出样例#1:
4
输入样例#2:
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 2
输出样例#2:
5
说明
【样例解释1】
小明选定让赞助商打通了 号宝藏屋。小明开发了道路 ,挖掘了 号宝 藏。开发了道路 ,挖掘了 号宝藏。还开发了道路 ,挖掘了 号宝 藏。工程总代价为:
【样例解释2】
小明选定让赞助商打通了 号宝藏屋。小明开发了道路 ,挖掘了 号宝 藏。开发了道路 ,挖掘了 号宝藏。还开发了道路 ,挖掘了 号宝 藏。工程总代价为:
【数据规模与约定】
对于 的数据: 保证输入是一棵树, 且所有的 都相等。
对于 的数据: 且所有的 都相等。
对于 的数据:
对于 的数据:
analysis
我们考虑一个最优解
-
显然这是一棵生成树,如果我们能构造出来 表示从 状态挖到 状态的最小花费
我们就可以按照层次加入点集更新答案了。 -
另 表示当前深度为 ,状态为 的最小花费
时间复杂度
如果预处理不够优秀的话 -
预处理我们再开一个数组 表示状态 挖到点 的最小花费(不考虑深度)
然后我们用边权更新 数组,再用 数组更新 数组即可
如果想要透彻理解此题,建议到1035719430。
code
状压DP
#include<bits/stdc++.h>
using namespace std;
const int maxn=15,maxs=(1<<12)+100;
const int inf=0x3f3f3f3f;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int e[maxn][maxn],h[maxn][maxs];
int f[maxn][maxs],g[maxs][maxs];
int main()
{
int n,m;read(n);read(m);
memset(e,0x3f,sizeof(e));
for (int i=1; i<=m; ++i)
{
int x,y,z;
read(x);read(y);read(z);
e[x][y]=e[y][x]=min(e[x][y],z);
}
int tot=(1<<n)-1;
for (int i=1; i<=n; ++i)
for (int s=1; s<=tot; ++s)
{
h[i][s]=inf;
if (!(s&(1<<i-1)))
for (int j=1; j<=n; ++j)
if (s&(1<<j-1))
h[i][s]=min(h[i][s],e[i][j]);
}
for (int s=1; s<=tot; ++s)
{
int t=s&(s-1);
while (t)
{
int x=s^t;
for (int i=1; i<=n; ++i)
if (x&(1<<i-1))
{
g[s][t]+=h[i][t];
if (g[s][t]>inf) g[s][t]=inf;
}
t=s&(t-1);
}
}
for (int i=1; i<=n; ++i)
for (int s=0; s<=tot; ++s)
f[i][s]=inf;
for (int i=1; i<=n; ++i)
f[1][1<<i-1]=0;
for (int i=2; i<=n; ++i)
for (int s=1; s<=tot; ++s)
{
int t=s&(s-1);
while (t)
{
int tmp=0;
if (g[s][t]<inf) tmp=g[s][t]*(i-1);
else tmp=inf;
if (f[i-1][t]<inf) f[i][s]=min(f[i][s],f[i-1][t]+tmp);
t=s&(t-1);
}
}
int ans=inf;
for (int i=1; i<=n; ++i)
ans=min(ans,f[i][tot]);
printf("%d\n",ans);
return 0;
}
模拟退火大法好
#include<bits/stdc++.h>
using namespace std;
const int maxn=30;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int n,m;
int g[maxn][maxn];
struct rec
{
int d[maxn],dep[maxn];
rec()
{
for (int i=1; i<=n; ++i)
d[i]=i,dep[i]=0;
}
rec(const rec &path)
{
memcpy(d,path.d,sizeof(d));
memset(dep,0,sizeof(dep));
swap(d[rand()%n+1],d[rand()%n+1]);
}
inline double dist()
{
double ans=0;
dep[d[1]]=1;
for (int i=2; i<=n; ++i)
{
double tmp=9999999;
for (int j=1; j<i; ++j)
if (g[d[j]][d[i]]!=0x3f3f3f3f && dep[d[j]]*g[d[j]][d[i]]<tmp)
tmp=dep[d[j]]*g[d[j]][d[i]],dep[d[i]]=dep[d[j]]+1;
if (tmp!=9999999) ans+=tmp;
else return tmp;
}
return ans;
}
};
inline int SA()
{
const double max_temper=10000;
const double dec=0.998;
double tmp=max_temper;
rec path;
while (tmp>0.01)
{
rec path2(path);
double del=path2.dist()-path.dist();
if (del<=0) path=path2;
else if (exp(-del/tmp)*RAND_MAX>=rand()) path=path2;
tmp*=dec;
}
return (int)path.dist();
}
int main()
{
srand(19260817);
read(n);read(m);
memset(g,0x3f,sizeof g);
for (int i=1; i<=m; ++i)
{
int x,y,z;
read(x);read(y);read(z);
g[x][y]=g[y][x]=min(g[x][y],z);
}
int ans=99999999,T=60;
while (T--) ans=min(ans,SA());
printf("%d\n",ans);
return 0;
}