• [JLOI2015] 管道连接


    题意:

    给定一张n个点m条边的无向图,有p个关键点,分成了c类。

    连通每条边有一个代价$w_i$,求最小代价使得同一类的关键点都联通。

    $nleq 1000,pleq 10$。

    题解:

    如果直接跑斯坦纳树会强行把所有关键点联通,但实际上两类关键点不一定非要联通。

    相当于我们求了一棵斯坦纳树,但要求的是斯坦纳森林。

    考虑森林中的每棵树,容易发现它们必须是若干类关键点集的并。

    换句话说,设一棵树的连通状态为S,对于第i类的关键点集$T_i$,要么$T_i subseteq S$,要么$T_i cap S=emptyset$。

    (我一开始只判断了前半部分居然拿到了85分,数据属实np)

    于是将所有满足要求的状态S拿出来重新dp即可。

    复杂度$O(n imes 3^p )$。

    套路:

    • 求斯坦纳森林$ ightarrow$对斯坦纳树再dp一遍。
    • 推条件时:注意充分性和必要性。

    代码:

    #include<bits/stdc++.h>
    #define maxn 1005
    #define maxm 10005
    #define inf 0x7fffffff
    #define ll long long
    #define rint register int
    #define debug(x) cerr<<#x<<": "<<x<<endl
    #define fgx cerr<<"--------------"<<endl
    #define dgx cerr<<"=============="<<endl
    
    using namespace std;
    int hd[maxn],to[maxm],nxt[maxm],cst[maxm];
    int n,m,p,dp[maxn][1<<10],inq[maxn],cnt;
    int sta[1<<10],res[maxn]; queue<int> q;
    
    inline int read(){
        int x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    inline void addedge(int u,int v,int w){
        to[++cnt]=v,cst[cnt]=w,nxt[cnt]=hd[u],hd[u]=cnt;
        to[++cnt]=u,cst[cnt]=w,nxt[cnt]=hd[v],hd[v]=cnt;
    }
    
    inline void spfa(int s){
        for(int i=1;i<=n;i++)
            if(dp[i][s]<=1e9)
                q.push(i),inq[i]=1;
        while(!q.empty()){
            int u=q.front(); inq[u]=0,q.pop();
            for(int i=hd[u];i;i=nxt[i]){
                int v=to[i],w=cst[i];
                if(dp[v][s]>dp[u][s]+w){
                    dp[v][s]=dp[u][s]+w;
                    if(!inq[v]) q.push(v),inq[v]=1;
                }
            }
        }
    }
    
    int main(){
        n=read(),m=read(),p=read();
        memset(dp,63,sizeof(dp));
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            addedge(u,v,w);
        }
        for(int i=1;i<=p;i++){
            int c=read(),d=read(); 
            res[c]|=(1<<i-1),dp[d][1<<(i-1)]=0;
        }
        for(int j=0;j<(1<<p);j++){
            for(int i=1;i<=n;i++)
                for(int k=j;k;k=(k-1)&j)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[i][j-k]);
            spfa(j);
        }
        memset(sta,63,sizeof(sta));
        for(int j=1;j<(1<<p);j++){
            bool flag=1;
            for(int i=1;i<=p;i++){
                if(!res[i]) continue;
                if((j&res[i])!=res[i] && (j&res[i])!=0) flag=0;
            } 
            if(!flag) continue;
            for(int i=1;i<=n;i++) sta[j]=min(sta[j],dp[i][j]);
        }
        for(int i=0;i<(1<<p);i++)
            for(int j=0;j<(1<<p);j++)
                sta[i|j]=min(sta[i|j],sta[i]+sta[j]);    
        printf("%d
    ",sta[(1<<p)-1]);
        return 0;
    }
    管道连接
  • 相关阅读:
    安装和配置nginx
    tomcat 生产发布脚本
    nginx 静态页面访问
    redis 搭建主从
    redis 安装
    perl 操作redis
    mysql 用户除了root一般不建议本地登录
    mysql创建用户
    mysql 查看排序集
    perl 安装Cpan
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13214907.html
Copyright © 2020-2023  润新知