Jeanny
寂兮,寥兮,独立不改,周行而不殆

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;
}

posted on 2025-09-06 09:25  Jeanny  阅读(8)  评论(0)    收藏  举报