信息學奧賽一本通 5.4 狀態壓縮動態規劃
阿新 • • 發佈:2018-08-23
har gif string cout lowbit -a 需要 names std
#loj 10170. 「一本通 5.4 例 1」騎士
看數據範圍n<=10,所以不是搜索就是狀壓dp,又因為搜索會超時所以用dp
dp[i][k][j]表示現已經放到第i行,前面共有k個,這一行狀態為j
so,dp[i][k][j]=dp[i-1][k-num[j]][t]
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define low_bit(x) x&-x; using namespaceView Codestd; inline long long read() { long long f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } long long dp[11][1001][1001];//dp[i][j][k]表示前i行放k個且第i行的狀態為j long long n,k,s[1001],num[1001]; long longcont(long long x) { long long c=0; while(x!=0) { x-=low_bit(x); c++; } return c; } int main() { n=read(),k=read(); long long ans=0; for(long long i=0;i<(1<<n)-1;i++) { if((i&(i<<1)))continue; s[++ans]=i; num[ans]=cont(i); } dp[0][1][0]=1; // for(long long i=1;i<=ans;i++) cout<<s[i]<<" ";cout<<endl; for(long long i=1;i<=n;i++) { for(long long j=1;j<=ans;j++) { for(long long kk=0;kk<=k;kk++) { if(kk>=num[j]) { for(long long t=1;t<=ans;t++) { if(s[t]&s[j]) continue; if(s[t]&(s[j]<<1)) continue; if(s[t]&(s[j]>>1)) continue; dp[i][j][kk]+=dp[i-1][t][kk-num[j]]; // cout<<dp[i][j][k]<<endl; } } } } } long long sum=0; for(long long i=1;i<=ans;i++) sum+=dp[n][i][k]; cout<<sum; }
#loj 10171. 「一本通 5.4 例 2」牧場的安排
一道比較普通的狀壓dp,關鍵點就是輸入時候怎麽處理荒草
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define mod 100000000 using namespace std; inline long long read() { long long f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } long long m,n; //dp[i][j]表示第i行用第j個狀態 //dp[i][j]+=dp[i-1][k] long long dp[20][10001]; long long ans[20];//ans[i]表示第i行有ans[i]個狀態 long long s[20][10001];//s[i][j]表示第i行第j個狀態 void init(long long h,long long t) { // cout<<t<<endl; for(long long i=0;i<=(1<<n)-1;i++) { if((i&(i<<1))||(i&(i>>1))||(i&t)) continue; s[h][++ans[h]]=i; } return; } int main() { m=read(),n=read(); for(long long i=1;i<=m;i++) { long long s=0; for(long long j=1;j<=n;j++) { long long x=read(); s=(s<<1)+1-x; } init(i,s); } for(long long i=1;i<=ans[1];i++) dp[1][i]=1; // for(long long i=1;i<=m;i++) // { // cout<<ans[i]<<endl; // for(long long j=1;j<=ans[i];j++) cout<<s[i][j]<<" "; // system("pause"); // } // for(long long i=2;i<=m;i++) { for(long long j=1;j<=ans[i];j++) { for(long long k=1;k<=ans[i-1];k++) { // cout<<i<<" "<<j<<" "<<k<<" "<<s[i][j]<<" "<<s[i-1][k]<<endl; if(s[i][j]&s[i-1][k]) continue; // if((s[i][j]>>1)&s[i-1][k]) continue; // if((s[i][j]<<1)&s[i-1][k]) continue; dp[i][j]+=dp[i-1][k]%mod; } } } long long sum=0; for(long long i=1;i<=ans[m];i++) sum+=dp[m][i]%mod,sum%=mod; cout<<sum%mod; }View Code
#loj 10172. 「一本通 5.4 練習 1」塗抹果醬
手打三進制運算即可
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define mod 1000000 using namespace std; inline long long read() { long long f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return f*ans; } long long n,m,k,a[6001],dg[10001]; bool check(long long x) { memset(dg,0,sizeof(dg)); long long ans=0; while(x!=0) { dg[++ans]=x%3; x/=3; } for(long long i=m;i>=2;i--) if(dg[i]==dg[i-1]) return false; return true; } long long s[50],ans; long long dg1[50],dg2[50]; void init() { ans=0; long long sry=1; for(long long i=1;i<=m;i++) sry*=3; for(long long i=0;i<=sry-1;i++) if(check(i)) s[++ans]=i; return; } long long dp[10001][50]; bool check2(long long x,long long y) { long long ans1=0,ans2=0; memset(dg1,0,sizeof(dg1)); memset(dg2,0,sizeof(dg2)); while(x!=0) { dg1[++ans1]=x%3; x/=3; } while(y!=0) { dg2[++ans2]=y%3; y/=3; } for(long long i=m;i>=1;i--) if(dg1[i]==dg2[i]) return false; return true; } long long cx(long long x) { for(long long i=1;i<=ans;i++) if(s[i]==x) return i; } long long book[51][51]; int main() { n=read(),m=read(); k=read(); for(long long i=1;i<=m;i++) { a[i]=read(); a[i]-=1; } long long kk=1,sum=0; for(long long i=m;i>=1;i--) { sum+=a[i]*kk; kk*=3; } if(check(sum)==false) { cout<<0; return 0; } init(); for(long long i=1;i<=ans;i++) for(long long j=i;j<=ans;j++) book[i][j]=book[j][i]=check2(s[i],s[j]); memset(dp,0,sizeof(dp)); for(long long i=1;i<=ans;i++) dp[1][i]=1; for(long long i=2;i<=k;i++) for(long long j=1;j<=ans;j++) for(long long k=1;k<=ans;k++) if(book[j][k]) { dp[i][j]+=(dp[i-1][k])%mod; dp[i][j]%=mod; // cout<<"行:"<<i<<" 狀態:"<<j<<" dp[i][j]:"<<dp[i][j]<<endl; } long long xxxx=cx(sum); long long p=dp[k][xxxx]%mod; if(p==0) { cout<<0; return 0; } memset(dp,0,sizeof(dp)); for(long long i=1;i<=ans;i++) dp[n][i]=1; for(long long i=n-1;i>=k;i--) for(long long j=1;j<=ans;j++) for(long long k=1;k<=ans;k++) if(book[j][k]) { dp[i][j]+=(dp[i+1][k])%mod; dp[i][j]%=mod; } long long p1=dp[k][xxxx]%mod; if(p1==0) { cout<<0; return 0; } cout<<(p1*p)%mod; return 0; }View Code
#loj 10173. 「一本通 5.4 練習 2」炮兵陣地
因為聯系是由三行,所以dp數組存行數外還要開兩位存這一位和上一位的狀態
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define lowbit(x) x&-x using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } int cont(int x) { int c=0; while(x!=0) { x-=lowbit(x); c++; } return c; } int ans[1101]; int s[101][1101]; int n,m; void init(int ha,int t) { for(int i=0;i<=(1<<m)-1;i++) { if(i&(i<<1)) continue; if(i&(i<<2)) continue; if(i&(i>>1)) continue; if(i&(i>>2)) continue; if(i&t) continue; s[ha][++ans[ha]]=i; } return; } int dp[4][1101][1101]; int main() { n=read(),m=read(); for(int i=1;i<=n;i++) { char str; int t=0; for(int j=1;j<=m;j++) { cin>>str; if(str==‘P‘) t=(t<<1); else if(str==‘H‘) t=(t<<1)+1; } init(i,t); } if(n==1) { cout<<ans[1]; return 0; } for(int i=1;i<=ans[2];i++) { for(int j=1;j<=ans[1];j++) { int s1=s[2][i],s2=s[1][j]; if(s1&s2) continue; dp[2][i][j]=max(dp[2][i][j],cont(s1)+cont(s2)); } } for(int i=3;i<=n;i++) { for(int j=1;j<=ans[i];j++) { for(int k=1;k<=ans[i-1];k++) { if(s[i][j]&s[i-1][k]) continue; for(int t=1;t<=ans[i-2];t++) { int s1=s[i][j],s2=s[i-1][k],s3=s[i-2][t]; if(s1&s2) continue; if(s1&s3) continue; if(s2&s3) continue; dp[i%3][j][k]=max(dp[i%3][j][k],dp[(i-1)%3][k][t]+cont(s1)); } } } } int sum=0; for(int i=1;i<=ans[n];i++) for(int j=1;j<=ans[n-1];j++) { if(s[n][i]&s[n-1][j]) continue; sum=max(sum,dp[n%3][i][j]); } cout<<sum; } /* 5 4 PHPP PPHH PPPP PHPP PHHP */View Code
#loj 10174. 「一本通 5.4 練習 3」動物園
這題的關鍵點就在小朋友只能看到5個數字
所以只需要狀壓這5個數字就好了
其實不需要斷環為鏈
因為你可以每次先假設一種狀態關系到0(其實是n),1,2,3,4
最後只需要輸出關於n的這種最大狀態
每次看一下需不需要放
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();} return ans*f; } int n,c; int e,f,l; int g[10001][32],dp[10001][32]; int main() { n=read(),c=read(); for(int p=1;p<=c;p++) { e=read(),f=read(),l=read(); int scared=0; for(int i=1;i<=f;i++) { int x=read(); x=(x-e+n)%n; scared|=(1<<x); } int happy=0; for(int i=1;i<=l;i++) { int x=read(); x=(x-e+n)%n; happy|=(1<<x); } int sry=31; for(int i=0;i<=31;i++) { if((i&happy)||((sry^i)&scared)) { g[e][i]++; // cout<<i<<endl; } } } int ans=0; for(int s=0;s<=15;s++) { for(int j=0;j<=31;j++) dp[0][j]=-(2<<30-1); dp[0][s<<1]=dp[0][s<<1|1]=0; for(int i=1;i<=n;i++) for(int ss=0;ss<=31;ss++) dp[i][ss]=max(dp[i-1][(ss&15)<<1],dp[i-1][(ss&15)<<1|1])+g[i][ss]; ans=max(ans,max(dp[n][s<<1|1],dp[n][s<<1])); } cout<<ans; }View Code
信息學奧賽一本通 5.4 狀態壓縮動態規劃