码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces 1430G Yet Another DAG Problem 状压dp

时间:2020-10-22 22:21:13      阅读:29      评论:0      收藏:0      [点我收藏+]

标签:i+1   vector   ble   lin   sizeof   def   first   一个   并且   

Codeforces 1430G Yet Another DAG Problem

题意

\(n\)个点\(m\)条边的有向无环图,每条边有边权\(w_i\),现在让你给每个点一个点权\(a_v\),对于第\(i\)条边\((x,y)\),写上一个数字\(b_i=a_x-a_y\)并且\(a_x>a_y\),使得\(\sum_{i=1}^{m}w_ib_i\)最小。

\(n\le 18\)

分析

\(\sum_{i=1}^{m}w_ib_i\)转换为\(\sum_{i=1}^{n}a_ic_i\),其中\(c_i\)为点\(i\)连出去的边的边权和减去连向点\(i\)的边权和。

考虑将点按点权分层,显然层数不会超过点数,所以点权赋值的范围可以为\([0,n-1]\),即最多将点分成\(n\)层,第\(i\)层的点的点权为\(i-1\)

\(dp[i][S]\)为前\(i\)层的点集为\(S\)并且\(\sum_{i \in S}a_ic_i\)的值最小,这样我们就可以枚举\(S\)的子集来转移,具体为枚举\(S\)的一个子集\(K\),若子集\(K\)中的点连出去的点都在集合\(S \oplus K\)中,那么子集\(K\)就可以作为\(S\)中第\(i\)层的点集,加上\(\sum_{j \in K}c_j \cdot (i-1)\)转移即可,子集\(K\)连出去的点的点集和\(\sum_{j \in k}c_i\)都可以预处理出来,时间复杂度为\(O(n3^n)\)

这样太慢了,考虑不枚举子集,改为枚举点来转移,对于每一层我们按拓扑序来枚举每个点,类似01背包那样选或不选这个点来转移,若选了这个点,类似之前的dp,这个点不能在集合\(S\)中且这个点连出去的点都在集合\(S\)中,因为是按拓扑序来枚举的,所以不会出现两个点之间有一条边且出现在同一层,时间复杂度为\(O(n^22^n)\)

\(O(n3^n)\) Code

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=1e5+10;
const int inf=1e9;
int n,m;
vector<int>g[20];
int a[20],c[20];
int sum[1<<20],gi[1<<20],dp[20][1<<20],f[20][1<<20];
int main(){
    cin>>n>>m;
    for(int i=1,x,y,w;i<=m;i++){
        cin>>x>>y>>w;
        --x;--y;
        c[x]+=w;
        c[y]-=w;
        g[x].pb(y);
    }
    for(int i=0;i<(1<<n);i++){
        for(int j=0;j<n;j++) if(i>>j&1){
            sum[i]+=c[j];
        }
        for(int j=0;j<n;j++) if(i>>j&1){
            for(int x:g[j]) gi[i]|=1<<x;
        }
    }
    memset(dp,0x3f3f3f,sizeof dp);
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int s=0;s<(1<<n);++s){
            for(int k=s;k;k=(k-1)&s){
                if((gi[k]&(s^k))==gi[k]){
                    int cost=dp[i-1][s^k]+sum[k]*(i-1);
                    if(cost<dp[i][s]){
                        dp[i][s]=cost;
                        f[i][s]=k;
                    }
                }
            }
        }
    }
    int s=(1<<n)-1;
    int mn=n;
    for(int i=1;i<=n;i++) if(dp[i][s]<dp[mn][s]) mn=i;
    for(int i=mn;i>=1;i--){
        int k=f[i][s];
        for(int j=0;j<n;j++) if(k>>j&1){
            a[j]=i-1;
        }
        s=s^k;
    }
    for(int i=0;i<n;i++) cout<<a[i]<<‘ ‘;
    cout<<endl;
    return 0;
}

\(O(n^22^n)\) Code

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=1e5+10;
const int inf=1e9;
int n,m;
vector<int>g[20],v;
int a[20],c[20],d[20],dp[20][20][1<<18],f[20][20][1<<18],bit[20];
int main(){
    cin>>n>>m;
    for(int i=1,x,y,w;i<=m;i++){
        cin>>x>>y>>w;
        --x;--y;
        c[x]+=w;
        c[y]-=w;
        g[x].pb(y);
        ++d[y];
        bit[x]|=1<<y;
    }
    queue<int>q;
    for(int i=0;i<n;i++) if(d[i]==0) q.push(i);
    while(!q.empty()){
        int u=q.front();q.pop();
        v.pb(u);
        for(int x:g[u]){
            if(--d[x]==0) q.push(x);
        }
    }
    memset(dp,0x3f3f3f,sizeof dp);
    dp[0][0][0]=0;
    for(int i=0;i<n;i++) for(int j=0;j<=n;j++) for(int k=0;k<(1<<n);k++){
        if(j==n){
            if(dp[i+1][0][k]>dp[i][j][k]){
                dp[i+1][0][k]=dp[i][j][k];
                f[i+1][0][k]=-1;
            }
            continue;
        }
        int x=v[j];
        if(dp[i][j+1][k]>dp[i][j][k]){
            dp[i][j+1][k]=dp[i][j][k];
            f[i][j+1][k]=-1;
        }
        if(!(k>>x&1)&&(bit[x]&k)==bit[x]){
            int nk=k^(1<<x);
            if(dp[i][j+1][nk]>dp[i][j][k]+c[x]*i){
                dp[i][j+1][nk]=dp[i][j][k]+c[x]*i;
                f[i][j+1][nk]=k;
            }
        }
    }
    int x=1,y=0,s=(1<<n)-1;
    for(int i=1;i<=n;i++) for(int j=0;j<=n;j++) if(dp[i][j][s]<dp[x][y][s]) x=i,y=j;
    while(x||y||s){
        if(f[x][y][s]==-1){
            if(y==0){
                x--;
                y=n;
            }else y--;
        }else{
            int ns=f[x][y][s];
            int k=ns^s;
            for(int i=0;i<n;i++) if(k>>i&1){
                a[i]=x;
            }
            s=ns;
            y--;
        }
    }
    for(int i=0;i<n;i++) cout<<a[i]<<‘ ‘;
    cout<<endl;
    return 0;
}

Codeforces 1430G Yet Another DAG Problem 状压dp

标签:i+1   vector   ble   lin   sizeof   def   first   一个   并且   

原文地址:https://www.cnblogs.com/xyq0220/p/13855612.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!