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