标签:i+1 vector ble lin sizeof def first 一个 并且
\(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)\)。
#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;
}
#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