zoukankan      html  css  js  c++  java
  • [Noip2017]宝藏

    [Noip2017]宝藏

    一.前言

    ​ 这道题的转移是真的有点怪……题目链接

    二.思路

    ​ 嘛,很明显是让你造一个生成树使得代价最小,看到 (1<=n<=12) 的时候就应该明白这道题的疯狂暗示了,状压 DP 嘛,TG老朋友了。

    ​ 观察到代价的计算,会发现一个 (K) 是一个至关重要的点,不把这个处理好了,就做不了。而这个 K也可以理解为深度。那么将状态设为 (f_{ij}) 为用状态为 j 的集合 (S) 构造出了一个最深深度为 i 的代价最小的生成树。

    ​ 然后很多题解所谓的"显而易见"的方程我反正是不能一下子想出来的,所以我会写的麻烦一些。计算 (f_{ij}) 那么

    • 肯定存在一个生成树代表 (f_{ij}) 的最优状态,它的点集设为 (S)
    • 这颗树一定有至少一个深度为 i 的点,这些点的点集我设为 (u)
    • 点集中所有点的父亲深度都为 (i-1)
    • 设一个树 (p) 为 这个生成树 删去 (u) 后的模样,其状态为 q

    于是 p 必然代表 (f_{i-1~q}) 的最优状态(措辞有点不严谨了见谅),抽象的说就是 最优减去必要的最少后还是最优,建议反复理解。于是可以反着来。

    ​ 枚举最深深度 i 从小到大,再枚举状态 j 和 j 的子集 k,可以得到转移方程

    [f_{ij}=min(f_{ij},f_{i-1~k}+trans_{jk}*(i-1)) ]

    其中 (trans) 为状态转移的最短距离和。实际上枚举子集 k 就是在找 u(u=j^k) ,由于 i 从小到大之前的状态都已经是最优,放心转移就好。

    ​ 然后初始化就是最深为1,状态只有一个点的时候老板帮忙,无花费,为 0。

    ​ 这题我当时觉得很奇怪的一点就在于,每一个状态在没有算完的时候是可能不合法的,因为 (trans)不会全部都接到最深的点,枚举的集合也不会都是最外围的点,但是最后最小的一定是合法的就对了。最优对最优的时候一定全部接到最深的点上,因为定义就是这么定义的,找的集合是最优时候的(上面的u)如果接其他点就不会是最优……(有点因果律)不像其他的 DP 题,很多时候只是不是最优,但是不会是不合法。

    ​ 以上。虽并非什么真知灼见,却也还是若有所思。

    三.CODE

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<fstream>
    #include<cmath>
    #include<cstring>
    using namespace std;
    int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0');
    	return res*f;
    }
    const int MAXN=1<<12,inf=707406378;
    int n,maxn,m;
    int trans[MAXN][MAXN],dis[13][13],f[13][MAXN];
    int main(){
    	memset(dis,127/3,sizeof(dis));
    	memset(f,127/3,sizeof(f));
    	n=read();m=read();
    	maxn=1<<n;
    	for(int i=1,x,y,v;i<=m;++i){
    		x=read()-1;y=read()-1;v=read();
    		dis[x][y]=dis[y][x]=min(dis[x][y],v);
    	}
    	for(int i=0;i<maxn;++i){
    		for(int j=i-1;j;j=(j-1)&i){
    			int k=i^j,stj[13],topj=0,topk=0,stk[13];
    			for(int p=0;p<n;++p){
    				if(j&(1<<p))stj[++topj]=p;
    				if(k&(1<<p))stk[++topk]=p;
    			}
    			while(topk){
    				int cnt=inf;
    				for(int p=topj;p>0;--p){
    					cnt=min(cnt,dis[stk[topk]][stj[p]]);
    				}
    				if(cnt!=inf)trans[i][j]+=cnt;
    				else{
    					trans[i][j]=inf;
    					break;
    				} 
    				topk--;
    			}
    		}
    	}
    	int ans=inf;
    	for(int i=0;i<n;++i)f[1][1<<i]=0;
    	ans=f[1][maxn-1];
    	for(int k=2;k<=n;++k){
    		for(int i=1;i<maxn;++i){
    			for(int j=i-1;j;j=(j-1)&i){
    				if(trans[i][j]!=inf&&f[k-1][j]!=inf)
    					f[k][i]=min(f[k][i],f[k-1][j]+(k-1)*trans[i][j]);
    			}
    		}
    		ans=min(ans,f[k][maxn-1]);
    	}
    	cout<<ans;
    	return 0;
    } 
    

    trans整的比较粗糙hhhh

  • 相关阅读:
    还是java中的编码问题
    java restful api
    编码方式
    LinkedHash
    Zoj 2562 More Divisors (反素数)
    spark复习总结03
    spark复习总结02
    spark复习总结01
    使用二进制解决一个字段代表多个状态的问题
    spark性能调优05-troubleshooting处理
  • 原文地址:https://www.cnblogs.com/clockwhite/p/13493335.html
Copyright © 2011-2022 走看看