1. 程式人生 > 其它 >jzoj5442. 【NOIP2017提高A組衝刺11.1】荒誕

jzoj5442. 【NOIP2017提高A組衝刺11.1】荒誕

技術標籤:題解DP狀壓

jzoj5442

Description

一個無向圖,最長的簡單路徑長度不超過10,要求每個點要麼被選,要麼有一個直接相連的點被選,求最小代價

Solution

30pts

暴力

45pts

加上樹形DP

滿分做法

還是考慮DP,由於原圖是無向圖,其 dfs 樹深度不超過10,因此考慮跟樹形DP一樣做

f x , s f_{x,s} fx,s 表示已經覆蓋完 尤拉序/dfs序 在x之前的點(x到根路徑上的點除外),x到根路徑上的點狀態為s,最小代價

往下傳時列舉祖先的狀態,分選和不選兩種情況轉移

往上傳時比較簡單

時間複雜度 O ( n ∗ 3 10 ) O(n*3^{10})

O(n310),然而狀態數明顯跑不滿,空間上根據深度滾動一下就可以

code

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
const int INF=1e9+7;
using namespace std;
const int N=2e4+10;
const int M=2.5e4+10;
const int K=59050;
int n,m,a[N],f[20][K],depth[N],cnt,q[N*2];
int tot,
last[N],mi[20],dis,ans; bool bz[N]; struct edge{ int st,en,next; }E[M*2]; void dfs(int x){ q[++cnt]=x; bz[x]=1; for(int p=last[x];p;p=E[p].next){ int y=E[p].en; if(bz[y])continue; depth[y]=depth[x]+1; dfs(y); q[++cnt]=x; } } void add(int x,int y){ E[++tot]=(edge){x,y,last[x]}; last[x]
=tot; } int main(){ freopen("absurdity.in","r",stdin); freopen("absurdity.out","w",stdout); mi[0]=1; fo(i,1,10)mi[i]=mi[i-1]*3; scanf("%d%d",&n,&m); fo(i,1,n)scanf("%d",&a[i]); fo(i,1,m){ int x,y; scanf("%d%d",&x,&y); add(x,y);add(y,x); } fo(i,1,n){ if(bz[i])continue; cnt=0; dfs(i); memset(f[0],127,sizeof(f[0])); dis=f[0][0]; f[0][0]=0; f[0][2]=a[i]; fo(i,2,cnt){ int x=depth[q[i]]; memset(f[x],127,sizeof(f[x])); if(x>depth[q[i-1]]){ fo(j,0,mi[x]-1){ if(f[x-1][j]==dis)continue; int s=j; bool flag=0; for(int p=last[q[i]];p;p=E[p].next){ int y=E[p].en; if(depth[y]>x)continue; if(j/mi[depth[y]]%3==2)flag=1; else s=(s/mi[depth[y]+1]*3+1)*mi[depth[y]]+s%mi[depth[y]]; } if(j/mi[x-1]==2)flag=1; else s=s%mi[x-1]+mi[x-1]; s+=mi[x]*2; if(flag)f[x][j+mi[x]]=min(f[x][j+mi[x]],f[x-1][j]); if(j/mi[x-1]!=2)f[x][j]=min(f[x][j],f[x-1][j]); f[x][s]=min(f[x][s],f[x-1][j]+a[q[i]]); } }else{ fo(j,0,mi[x+1]-1){ f[x][j]=min(f[x+1][j+mi[x+1]],f[x+1][j+2*mi[x+1]]); } } } ans+=min(f[0][1],f[0][2]); } printf("%d\n",ans); return 0; }