Codeforces 1430G Yet Another DAG Problem 状压dp

Posted xyq0220

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 1430G Yet Another DAG Problem 状压dp相关的知识,希望对你有一定的参考价值。

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)最小。

(nle 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的主要内容,如果未能解决你的问题,请参考以下文章

Educational Codeforces Round 88 (Rated for Div. 2) D. Yet Another Yet Another Task

CodeForces - 393E Yet Another Number Sequence

Codeforces 903 G. Yet Another Maxflow Problem

[Codeforces 863D]Yet Another Array Queries Problem

codeforces CF903G Yet Another Maxflow Problem 线段树

Codeforces 524F And Yet Another Bracket Sequence 哈希