(权限题)NFLSoj #103: 小D的道路 题解

感觉有点像网络流,但不会搞
看到数据范围又不由得往状压dp方面想
我们可以发现,一个图是优美的,则它的形状一定是一条从1~n的链,其中链上的每个点挂着一个子图
考虑dp[Mask][i]表示当前已经将Mask中的节点加入地图,主链的末端点是i的最大边权
考虑向后转移,有两种情况:
1. 向主链的后面添加一个节点,我们可以枚举Mask之外的每个点u,看i,u之间是否有边
dp[Mask|u][u]=min(dp[Mask|u][u],dp[Mask][i]+len(i,u)) d p [ M a s k | u ] [ u ] = m i n ( d p [ M a s k | u ] [ u ] , d p [ M a s k ] [ i ] + l e n ( i , u ) )
2. 在u的下面挂一个子图,我们可以枚举Mask之外的子集
dp[Mask|sub][i]=min(dp[Mask|sub][i],dp[Mask][i]+sum[sub|i]) d p [ M a s k | s u b ] [ i ] = m i n ( d p [ M a s k | s u b ] [ i ] , d p [ M a s k ] [ i ] + s u m [ s u b | i ] )
其中sum[S]表示连接点集S中的点的边的权值和,这个是可以 O(2nm) O ( 2 n ∗ m ) 预处理的
所以我们的总复杂度为 O(2nm+2nn+3n) O ( 2 n ∗ m + 2 n ∗ n + 3 n )
用spfa序转移足以通过此题

#include <bits/stdc++.h>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pLL pair<LL,LL>
#define pii pair<double,double>
#define pb push_back
#define mp make_pair
#define pf push_front
#define LOWBIT(x) x & (-x)
using namespace std;

const int INF=2e9;
const LL LINF=2e16;
const int magic=348;
const double eps=1e-5;
const int MOD=998244353;
const double pi=acos(-1);

inline int getint()
{
    bool f;char ch;int res;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

vector<Pair> v[48];int ga[48][48];
queue<int> q;bool inq[200048][28];
int dp[200048][28],sum[200048];
int n,m;

inline void init_sum()
{
    int Mask,i,j;
    for (Mask=0;Mask<=(1<<n)-1;Mask++)
    {
        for (i=1;i<=n;i++)
            if (Mask&(1<<(i-1)))
                for (j=0;j<int(v[i].size());j++)
                    if (Mask&(1<<(v[i][j].x-1))) sum[Mask]+=v[i][j].y;
        sum[Mask]>>=1;
    }
}

int main ()
{
    int i,x,y,c,Mask,rem,sub,cur,tocur,toMask;
    n=getint();m=getint();memset(ga,0,sizeof(ga));
    for (i=1;i<=m;i++)
    {
        x=getint();y=getint();c=getint();
        v[x].pb(mp(y,c));v[y].pb(mp(x,c));
        ga[x][y]=ga[y][x]=c;
    }
    init_sum();
    memset(dp,0,sizeof(dp));memset(inq,false,sizeof(inq));
    q.push(1);q.push(1);inq[1][1]=true;
    while (!q.empty())
    {
        Mask=q.front();q.pop();cur=q.front();q.pop();
        inq[Mask][cur]=false;
        for (i=1;i<=n;i++)
            if (!(Mask&(1<<(i-1))) && ga[cur][i])
            {
                tocur=i;toMask=(Mask|(1<<(i-1)));
                if (dp[toMask][tocur]<dp[Mask][cur]+ga[cur][tocur])
                {
                    dp[toMask][tocur]=dp[Mask][cur]+ga[cur][tocur];
                    if (!inq[toMask][tocur]) inq[toMask][tocur]=true,q.push(toMask),q.push(tocur);
                }
            }
        rem=(1<<n)-1-Mask;
        for (sub=rem;sub;sub=rem&(sub-1))
        {
            tocur=cur;toMask=(Mask|sub);
            if (dp[toMask][tocur]<dp[Mask][cur]+sum[sub|(1<<(cur-1))])
            {
                dp[toMask][tocur]=dp[Mask][cur]+sum[sub|(1<<(cur-1))];
                if (!inq[toMask][tocur]) inq[toMask][tocur]=true,q.push(toMask),q.push(tocur);
            }
        }
    }
    printf("%d\n",dp[(1<<n)-1][n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值