题目大意:有n个物品,有两辆车载重分别是c1,c2.问需要多少趟能把物品运完。 (1 ≤ Ci ≤ 100,1 ≤ n ≤ 10,1 ≤ wi ≤ 100).
题解:n小思状压。我们先把所有一次可以拉走的状态ini预处理好,然后把这些状态当做一个个物品跑背包就行了。
合并的转移:dp[j|ini[i]]=min(dp[j|ini[i]],dp[j]+1);
就是如果状态j与ini[i]不冲突,则可以从状态j运一趟变为j|ok[i]。
然后正确性可以YY一下:为什么可以看成是物品?并一下就可以?是因为ini中包含了所有的可能情况,我们也全都考虑了转移(这句话看不懂就算了我语文不好= =)
注意几个细节:在check的时候随时注意sm剪枝,f的边界是f[0]=true(无论c1,c2是多少重为0的东西总是可以的),dp的边界是dp[0]=0(全都不搬运能收获0),还有注意判重。然后就差不多了。
不过我还有个问题:为什么inf=-1u>>1就一直爆?
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<queue> 6 #include<cstring> 7 #define PAU putchar(' ') 8 #define ENT putchar('\n') 9 using namespace std; 10 const int maxn=1<<11,maxv=100+10,inf=1<<30; 11 int w[10],n,c1,c2,dp[maxn],ini[maxn]; 12 bool check(int val){ 13 static bool f[maxv];memset(f,false,sizeof(f));f[0]=true; 14 int sm=0; 15 for(int i=0;i<n;i++) if((1<<i)&val){ 16 sm+=w[i];if(sm>c1+c2) return false; 17 for(int j=c1;j>=w[i];j--) f[j]=f[j]|f[j-w[i]]; 18 } 19 for(int i=0;i<=c1;i++) if(f[i]&&sm-i<=c2) return true; 20 return false; 21 } 22 inline int read(){ 23 int x=0,sig=1;char ch=getchar(); 24 while(!isdigit(ch)){if(ch=='-')sig=-1;ch=getchar();} 25 while(isdigit(ch))x=10*x+ch-'0',ch=getchar(); 26 return x*=sig; 27 } 28 inline void write(int x){ 29 if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x; 30 int len=0,buf[15];while(x)buf[len++]=x%10,x/=10; 31 for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return; 32 } 33 void init(){ 34 int Case=read(); 35 for(int cas=1;cas<=Case;cas++){ 36 n=read();c1=read();c2=read(); 37 for(int i=0;i<n;i++) w[i]=read(); 38 int cnt=0,num=1<<n; 39 for(int i=0;i<num;i++){ 40 dp[i]=inf; 41 if(check(i)) ini[cnt++]=i; 42 } 43 dp[0]=0; 44 for(int i=0;i<cnt;i++) 45 for(int j=0;j<num;j++) if(!(ini[i]&j)) 46 dp[ini[i]|j]=min(dp[ini[i]|j],dp[j]+1); 47 printf("Scenario #%d:\n%d\n\n",cas,dp[(1<<n)-1]); 48 } 49 return; 50 } 51 void work(){ 52 return; 53 } 54 void print(){ 55 return; 56 } 57 int main(){init();work();print();return 0;}