参考雨巨视频:https://www.bilibili.com/video/BV1z441137v6?p=1
1.互不侵犯
参考博客:https://www.cnblogs.com/ljy-endl/p/11627018.html //tql
#include<bits/stdc++.h> #define ll long long using namespace std; int n,K,m; ll f[17][155][155],ans=0; int num[155],s[155]; int s0=0; // f[i][j][k]表示第i行 ,状态为 j ,已经用了 k 个国王所产生的方案数 void pre(){//预处理 int i,j,k; memset(f,0,sizeof(f)); for(int i=0;i<m;++i){ if(i&(i<<1)) continue; //两个国王相邻(会打起来 例: 1001101 k=0; for(int j=0;j<n;++j) if(i&(1<<j)) k++; //记录方案有几个国王 s[++s0]=i; //预处理有效的方案 num[s0]=k; //保存每一种方案可放国王数量 } } void dp(){ f[0][1][0]=1; //第0行放0个国王的方案数为1 for(int i=1;i<=n;++i){ //枚举行 for(int j=1;j<=s0;++j){ //枚举可行状态 for(int k=0;k<=K;++k){ //枚举国王数量 if(k<num[j]) continue; for(int t=1;t<=s0;++t){ //枚举上一行的状态 // s[t]&s[j] :s[t]至少有一个在国王在s[j]上面 例:s[t]: 100010001 s[j]: 001010001 // s[t]&(s[j]<<1): s[t]至少有一个在国王在s[j]右上方 例:s[t]: 100010001 s[j]: 100100001 // s[t]&(s[j]<<1): s[t]至少有一个在国王在s[j]左上方 例:s[t]: 100010001 s[j]: 100001001 if((!(s[t]&s[j]))&&(!(s[t]&(s[j]<<1)))&&(!(s[t]&(s[j]>>1)))) f[i][j][k]+=f[i-1][t][k-num[j]]; //不冲突,加上 上一行 t状态下的[k-num[j]]个国王的方案数 } } } } for(int i=1;i<=s0;++i) ans+=f[n][i][K]; //累加统计 cout<<ans<<" "; } int main(){ cin>>n>>K; m=(1<<n); pre(); dp(); return 0; }
2.No Change
参考博客:https://ac.nowcoder.com/discuss/633882
#include<bits/stdc++.h> #define ll long long using namespace std; const int qs=1e5+7; const int inf =0x3f3f3f3f; int n,m,s[qs],sum,con[20],f[qs]; //f[i]表示 状态i下可以买到的物品的数量 int Find(int x,int k){//二分找到可以买到的最大价值的下标 int l=x,r=m,ans=x; while(l<=r){ int mid=((l+r)>>1); if(s[mid]-s[x]<=k) ans=mid,l=mid+1; else r=mid-1; } return ans; } int cal(int x){ //状态x下每个1的位置代表用掉的硬币下标 int p=sum; for(int i=0;i<n;++i){ if((x>>i)&1) p-=con[i]; } return p; } int main(){ cin>>n>>m; sum=0; for(int i=0;i<n;++i) cin>>con[i],sum+=con[i]; //统计前缀和,用来二分取区间 for(int i=1;i<=m;++i) cin>>s[i],s[i]+=s[i-1]; for(int i=0;i<(1<<n);++i) f[i]=-inf; f[0]=0; for(int i=0;i<(1<<n);++i){ //枚举(1<<n)种状态 for(int j=0;j<n;++j){ //枚举n枚硬币 //在i状态下没有使用j硬币 if(!((i>>j)&1)) continue; if(f[i-(1<<j)]>=0) //找到过去状态,计算购买东西数量 f[i]=max(f[i],Find(f[i-(1<<j)],con[j])); } } int ans=-1; for(int i=0;i<(1<<n);++i){ if(f[i]==m) ans=max(ans,cal(i)); } cout<<ans<<' '; return 0; }
//理解了一整天 划水了一整天
//雨巨讲的太好了,我竟然也能理解QAQ