E.环的计数
1.到达i这个点有几条路 , 将方案数进行传递
2.一个环中的起点可以是任意一个,是否需要记录起点作为一个状态,传递下去?
3.难点:从2可以想到,这个环中如果一定包含i这个点,则这个点一定可以作为起点。我们可以设置一个环中最值作为环中的起点? 这样就不用枚举起点了,而且也不会算重。
4.复杂度2^191919是对的
5.坑点在于,需要考虑重复情况。第一,一个点作为起点之后统计出来的环,可以往左走,也可以往右走,因此算出来的方案数得除以2. 第二,两个端点连接多条边,会构成双元环,是我们统计的内容。但是两个点只有一条边,也会被我们统计到,因此需要减掉.
#include <bits/stdc++.h>
#define int long long
#define N 2005
using namespace std;
int n, m, dp[1 << 19][20], g[20][20], ans, a, b;
int lowbit(int x) { return x & (-x); }
signed main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> a >> b;
g[a][b] = g[b][a] = 1;
}
for (int i = 1; i <= n; i++) dp[1 << (i - 1)][i] = 1;
for (int i = 1; i <= (1 << n) - 1; i++) {
for (int j = 1; j <= n; j++) { // j是以谁结尾
if (dp[i][j] == 0)
continue;
if ((1 << (j - 1) & i) == 0)
continue;
for (int k = 1; k <= n; k++) {
if (g[j][k] == 0 || lowbit(i) > 1 << (k - 1))
continue;
if ((1 << (k - 1) & i) == 0) //未到的点
dp[i | (1 << (k - 1))][k] += dp[i][j];
else if ((1 << (k - 1)) == lowbit(i)) //已有的点
ans += dp[i][j];
}
}
}
ans -= m;
cout << ans / 2 << endl;
return 0;
}
F.宝藏
1.比较容易分析出来,最终的形态一定是一棵树。每一个节点v一定有一个父亲u,答案是$w(u,v) \times $树的深度
2.我们可以枚举树当前这一层中有哪些节点,和上一层的节点相连接选择其中最小的边值。但是复杂度不对。
3.这里有个trick:当求解的答案更大时,对最终的最优解没有影响,因此我可以不枚举前一层到底有哪些点,之前的所有点集都枚举一遍,最优解不受影响。
因此,枚举子集作为当前层的点集k,前i层的状态j,j^i为前i-1层的状态。求最小权值之和✖当前层的树深度。
3.复杂度:枚举树的深度,枚举前i层的状态,枚举子集作为当前层的集合,3^n✖n + 预处理3^n ✖ n ✖ n = 3^12 × 12 × 12 = 76,527,504
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int M=(1<<12),inf=0x3f3f3f3f;
int cost[M][M],n,m,c[12][12];
long long dp[12][M],ans=inf;
void init(){
for(int i=1;i<(1<<n);i++){//i当前及前面所有点构成的集合
for(int j=i;j;j=(j-1)&i){//j当前这层的点构成的集合
if(j==i)continue;
for(int t=0,tmp;t<n;t++,tmp=inf){
if(!(((i^j)>>t)&1))continue;//t是不包括当前层的点
for(int l=0;l<n;l++)if((j>>l)&1)tmp=min(tmp,c[l][t]);//l当前这层的点
if(tmp>=inf){cost[j][i]=inf;break;}
else cost[j][i]+=tmp;//j当前层的集合,当前层及前面所有层的点的集合
}
}
}
return;
}
long long DP(int x){
for(int i=0;i<12;i++)for(int j=0;j<M;j++)dp[i][j]=inf;//dp值取min,所以先清空
dp[0][1<<x]=0;
long long res=inf;
for(int i=1;i<n;i++){
for(int j=1;j<(1<<n);j++){
for(int k=j;k;k=(k-1)&j){//当前i层的状态k,前i层的状态j,构成的花费cost[k][j]
if(k==j)continue;
dp[i][j]=min(dp[i][j],dp[i-1][k]+1ll*i*cost[k][j]);
}
if(j==(1<<n)-1)res=min(res,dp[i][j]);
}
}
return res;
}
int main(){
scanf("%d %d",&n,&m);
if(n==1){
printf("0\n");
return 0;
}
for(int i=0;i<n;i++)for(int j=0;j<n;j++)c[i][j]=inf;
for(int i=1,u,v,w;i<=m;i++){
scanf("%d %d %d",&u,&v,&w),u--,v--;
c[v][u]=c[u][v]=min(c[u][v],w);
}
init();
for(int i=0;i<n;i++)ans=min(ans,DP(i));
printf("%lld\n",ans);
return 0;
}
浙公网安备 33010602011771号