题意:
给n1个物品1,和n2个物品2,求最少的操作次数,使物品2变成物品1(可以拆分物品2,也可以聚拢)
分析:
数据范围小->状压或暴搜,考虑暴搜,不好存状态,因为把两个物品拼接起来物品的数量会改变。于是只能考虑状压。(考场上还有半个小时的时候极其兴奋地去打状压,哇塞我好聪明,哇塞我好强,竟然打出来了耶,最后发现,题看错了。。。看漏了可以拆分物品2来变成物品1)
这是我自认为自己绝顶聪明的代码(呕)
#include<bits/stdc++.h> using namespace std; #define N 15 int n1,n2,a[N],b[N],dp[N][(1<<N)],pos[N]; int get(int x) { int ans=0; while(x){ if(x&1) ans++; x>>=1; } return ans; } int check(int x,int y,int val) { int rx=x,ry=y,cnt=0,wei=0,tot=0; while(ry){ wei++; if((!(ry&1))&&(rx&1)) return -1; if((!(rx&1))&&(ry&1)) pos[++cnt]=wei; rx>>=1,ry>>=1; } for(int i=1;i<=cnt;i++) tot+=b[pos[i]]; if(tot!=val) return -1; return cnt; } int main() { freopen("Miku.in","r",stdin); freopen("Miku.out","w",stdout); scanf("%d",&n1); for(int i=1;i<=n1;i++) scanf("%d",&a[i]); scanf("%d",&n2); for(int i=1;i<=n2;i++) scanf("%d",&b[i]); if(n1==1) { printf("%d\n",n2-1); return 0 ; } memset(dp,0x7f7f7f,sizeof(dp)); for(int i=0;i<(1<<n2);i++){ int xx=check(i,(1<<n2)-1,a[1]); if(xx!=-1) dp[1][i]=xx-1; } for(int i=1;i<=n1-1;i++) for(int j=0;j<(1<<n2);j++) for(int k=0;k<(1<<n2);k++){ int xx=check(j,k,a[i+1]); if(get(j)<get(k)&&xx!=-1) dp[i+1][j]=min(dp[i+1][j],dp[i][k]+xx-1); } printf("%d\n",dp[n1][0]); } /* 4 5 3 6 4 7 2 3 4 3 4 1 1 ans 3 */
所以说正解是怎么dp的呢?拆分的聚拢的物品数频频在变,无论怎样都不好转移,去找题的特征与性质。
我们可以发现(不,我没有发现),最多的操作次数是n1+n2-2次就是把它所有聚拢又拆分后的次数。然后我们发现, 如果初始状态中挑出 k1块的面积刚好等于目标状态中k2块的面积, 我们可以单独让这几块通过 k1+k2-2操作达到目标状态, 剩余的n1-k1与n2-k2可以通过分裂合并达到目标状态, 总操作数会变为 n1+n2-4次,就减少了两次。(引用)
所以我们要做的是找到尽量多的这样的关系来减少使用次数。先把n2的物品设为负数,sum是辅助数组帮助记录有没有完成从k1拼成k2个,若sum==0,则说明又多了一对上述的关系,总操作次数可以-2。最终答案就是总操作次数-可以减少的操作次数
#include<bits/stdc++.h> using namespace std; int n1,n2,a[30],sum[(1<<20)+5],dp[(1<<20)+5]; int main() { freopen("Miku.in", "r", stdin); freopen("Miku.out", "w", stdout); scanf("%d",&n1); for(int i=1;i<=n1;i++) scanf("%d",&a[i]); scanf("%d",&n2); for(int i=1;i<=n2;i++) scanf("%d",&a[i+n1]),a[i+n1]=-a[i+n1]; int n=n1+n2; for(int i=1;i<(1<<n);i++){//1!! for(int j=1;j<=n;j++){ if(i&(1<<j))//j xuan dp[i]=max(dp[i],dp[i^(1<<j)]),sum[i]+=a[j]; } if(sum[i]==0) dp[i]++; } printf("%d\n",n-2*dp[(1<<n)-1]); } /* 1 6 3 1 2 3 */