标签:不难 logs mat 不能 能力 状态转移方程 down while 题解
QAQ……由于没报上名并没能亲自去,自己切一切题聊以慰藉吧……
可能等到省选的时候我就没有能力再不看题解自己切省选题了……辣鸡HZ毁我青春
地球人都会做,懒得写题解了……
分类讨论+递归就行了,没啥思维含量,略。
这题好劲啊……
\(O(n^2)\)暴力就行了,水题。
看到数据范围一眼\(O^*(3^n)\)状压DP,其中\(3^n\)来自枚举子集的子集。做法好像有很多,比如ryf的做法就比我快了10倍……日渐辣鸡的窝
定义\(f_{i,j,S}\)表示以\(i\)为根的子树,\(i\)在整棵树里的深度是\(j\),集合\(S\)中的所有点都在这棵子树中时这棵子树的最小总代价。
不难写出转移方程:
\[f_{i,j,S}=\min_{T\subsetneq S,k\in T}\{f_{k,j+1,T}+f_{i,j,S-T}+j\times w_{i,k}\}\]
(这个转移方程是在枚举与\(i\)相邻的一个点\(k\)并把以\(k\)为根的子树分出去)
然而你发现这个DP是\(O(n^3 3^n)\)的,并不能跑过去。
考虑优化转移,如果把转移方程中与\(S\)无关的部分拿出来并定义一个辅助数组
\[g_{i,j,T}=\min_{k\in T}\{f_{k,j+1,T}+j\times w_{i,k}\}\]
的话,我们就可以把状态转移方程改写成
\[f_{i,j,S}=\min_{T\subsetneq S}\{f_{i,j,S-T}+g_{i,j,T}\}\]
这样就可以把复杂度降到\(O(n^2 3^n)\)了,由于常数不大,并不需要卡常。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define bit(x) (1<<((x)-1))
using namespace std;
const int INF=0x3f3f3f3f;
int n,m,w[15][15],f[15][15][(1<<12)+1],g[15][15][(1<<12)+1];
int main(){
scanf("%d%d",&n,&m);
memset(w,63,sizeof(w));
memset(f,63,sizeof(f));
memset(g,63,sizeof(g));
while(m--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
w[x][y]=w[y][x]=min(w[x][y],z);
}
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j][bit(i)]=0;
for(int j=n-1;j;j--)for(int s=0;s<(1<<n);s++)for(int i=1;i<=n;i++){
for(int k=1;k<=n;k++)if((bit(k)|s)==s&&w[i][k]<INF)
g[i][j][s]=min(g[i][j][s],f[k][j+1][s]+w[i][k]*j);
for(int t=s&(s-1);;(--t)&=s){
f[i][j][s]=min(f[i][j][s],f[i][j][s^t]+g[i][j][t]);
if(!t)break;
}
}
int ans=INF;
for(int i=1;i<=n;i++)ans=min(ans,f[i][1][(1<<n)-1]);
printf("%d",ans);
return 0;
}
这题真是劲啊……比往年的数据结构NB到不知哪儿去了……
标签:不难 logs mat 不能 能力 状态转移方程 down while 题解
原文地址:http://www.cnblogs.com/hzoier/p/7857587.html