BZOJ 4006 [JLOI2015]管道连接

斯坦纳树DP。

首先记f[i][sta]表示经过节点i节点状态sta的最小花费,进行斯坦纳树DP

再记g[s]表示颜色状态s的最小花费,注意是颜色状态,每一位表示一个颜色集合,那么g[s]可以从f[i][sta]O(p2p)的时间转移过来(为什么我的复杂度是这个可以研究一下代码- -)

显然直接用g[2p1]数组作为答案是不对的,因为不同频道可以不连通。于是再DP一次,就能成功地把不连通的情况考虑进去啦,方程:

g[sta]=min(g[sta],g[s]+g[stas]),(ssta)

为什么这个方程是正确的?因为他能够直接相加而不考虑交点呀。。。

代码又丑又慢,改了半天,跑了十多秒QAQ

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 20000005
#define N 1005
#define M 3005
#define P 11
using namespace std;
struct edge{int next,to,v;}e[M<<1];
struct color{int col,id;}c[P];
queue<int> q;
int cnt=1, n, m, p, last[N], col_sta[P], f[N][1<<P], g[1<<P], key_cnt=0, state=0;
bool inq[N];
void add(int a, int b, int c)
{
    e[++cnt]=(edge){last[a],b,c};
    last[a]=cnt;
}
void SPFA(int k)
{
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        inq[x]=0;
        for(int i = last[x]; i; i=e[i].next)
        {
            int y=e[i].to;
            if(f[x][k]+e[i].v<f[y][k])
            {
                f[y][k]=f[x][k]+e[i].v;
                if(!inq[y])
                {
                    inq[y]=1;
                    q.push(y);
                }
            }
        }
    }
}
bool cmp(color a, color b){return a.col<b.col;}
int main()
{
    memset(f,63,sizeof(f));
    scanf("%d%d%d",&n,&m,&p);
    for(int a, b, v, i = 0; i < m; i++)
    {
        scanf("%d%d%d",&a,&b,&v);
        add(a,b,v);
        add(b,a,v);
    }
    for(; key_cnt < p; key_cnt++)
        scanf("%d%d",&c[key_cnt+1].col,&c[key_cnt+1].id);
    sort(c+1,c+1+key_cnt,cmp);
    int col_cnt=0;
    for(int i = 1; i <= p; i++)
        if(c[i].col!=c[i-1].col) col_cnt++;
    c[0].col=c[1].col;
    for(int i = 1, pos=1; i <= col_cnt; i++)
    {
        while(c[pos].col==c[pos-1].col)
            c[pos-1].col=i,pos++;
        c[pos-1].col=i;
        pos++;
    }
    for(int i = 1; i <= p; i++)
    {
        col_sta[c[i].col]|=1<<(i-1);
        f[c[i].id][1<<(i-1)]=0;
    }
    for(int sta=1; sta<(1<<key_cnt); sta++)
    {
        for(int j = 1; j <= n; j++)
        {
            for(int s=sta&(sta-1); s; s=(s-1)&sta)
                f[j][sta]=min(f[j][sta],f[j][s]+f[j][sta-s]);
            if(f[j][sta]<INF)
            {
                q.push(j);
                inq[j]=1;
            }
        }
        SPFA(sta);
        for(int i = 1; i <= n; i++)
            f[0][sta]=min(f[0][sta],f[i][sta]);
    }
    for(int i = 1; i < (1<<col_cnt); i++)
    {
        int j = i, t = 0;
        for(int k = 1; j; j>>=1, k++)
        {
            if(j&1)
                t|=col_sta[k];
        }
        g[i]=f[0][t];
    }
    for(int sta=1; sta<(1<<col_cnt); sta++)
        for(int s=sta&(sta-1); s; s=(s-1)&sta)
            g[sta]=min(g[sta],g[s]+g[sta-s]);
    printf("%d\n",g[(1<<col_cnt)-1]);
}
发布了325 篇原创文章 · 获赞 98 · 访问量 12万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 像素格子 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览