P5911 [POI2004]PRZ 题解
N=16,首先考虑将其作为状压维度
可以用其来记录哪一些人已经渡河,哪一些人没有渡河。
每一次状态转移就是一次渡河,可以暴力枚举本次渡河的人是哪一些。
利用记忆化搜索可以更好地实现这个过程。
在枚举上一个合法状态的时候可以利用枚举子集的方式来完成,以此降低一部分复杂度。实际上枚举子集的复杂度依然是2^n
更新:枚举子集的总复杂度为3^n
枚举子集的方法看其他blog
代码如下
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 inline int r() 7 { 8 int s=0,k=1;char c=getchar(); 9 while(!isdigit(c)) 10 { 11 if(c=='-')k=-1; 12 c=getchar(); 13 } 14 while(isdigit(c)) 15 { 16 s=s*10+c-'0'; 17 c=getchar(); 18 } 19 return s*k; 20 } 21 int m,n,t[20],w[20],f[66666],chk[66666],cst[66666]; 22 bool check(int x) 23 { 24 //if(chk[x]!=-1)return chk[x]; 25 int now=0,nd=0; 26 while(x) 27 { 28 now++; 29 if(x&1)nd+=w[now]; 30 if(nd>m)return chk[x]=0; 31 x>>=1; 32 } 33 return chk[x]=1; 34 } 35 int cost(int x) 36 { 37 //if(cst[x]!=-1)return cst[x]; 38 int now=0,nd=0; 39 while(x) 40 { 41 now++; 42 if(x&1)nd=max(nd,t[now]); 43 x>>=1; 44 } 45 return cst[x]=nd; 46 } 47 int dfs(int state) 48 { 49 if(!state)return 0; 50 if(f[state])return f[state]; 51 int mini=1e9; 52 for(int i=state;i;i=(i-1)&state) 53 if(check(i)) 54 { 55 mini=min(mini,dfs(state^i)+cost(i)); 56 } 57 // cout<<"state: "<<state<<" "<<mini<<endl; 58 return f[state]=mini; 59 } 60 int main() 61 { 62 memset(chk,-1,sizeof(chk)); 63 memset(cst,-1,sizeof(cst)); 64 m=r();n=r(); 65 for(int i=1;i<=n;i++) 66 { 67 t[i]=r(); 68 w[i]=r(); 69 } 70 for(int i=1;i<=n;i++) 71 f[1<<(i-1)]=t[i]; 72 for(int i=1;i<=n;i++) 73 for(int j=1;j<=n;j++) 74 { 75 if(i!=j) 76 { 77 int tmp=(1<<(i-1))|(1<<(j-1)); 78 if(check(tmp))f[tmp]=cost(tmp); 79 else f[tmp]=t[i]+t[j]; 80 } 81 } 82 cout<<dfs((1<<n)-1); 83 }
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/14999676.html