題目大意:
給你一些物品。每個物品有相應的重量。給你兩輛載重不一定相同的車。問你最少要多少趟才能把所有物品運完。
思路:
狀態壓縮dp。二進制表示物品狀態。0表示還沒運走。1表示已經運走了。那麼就可以枚舉出兩輛車一趟可一運出的狀態。由於物品是一趟一趟運出來的。所以就可以由一個狀態通過兩輛車一趟的狀態轉移到另一個狀態。
dp[i]=MIN(dp[k]+1)。k可以由兩車一趟轉移到i。
詳細請看代碼。
該題我在網上看到多分代碼,每種代碼有不同的思路,方案。值得借鑑
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
using namespace std;
int a[20],n;
bool vis[110];
int d[1<<10+10];
int sta[1<<10+10];
int cnt=0,c1,c2;
int judge(int x)
{
int i,j;
int sum=0;
for(i=0;i<=c1;i++)
vis[i]=0;
vis[0]=1;
for(i=0;i<n;i++)
{
if(x&(1<<i))
{
sum+=a[i];
for(j=c1-a[i];j>=0;j--) // 有dp的感覺
{
if(vis[j])
vis[j+a[i]]=1;
}
}
}
for(i=0;i<=c1;i++)
{
if(vis[i]&&sum-i<=c2)
return 1;
}
return 0;
}
/*void dfs(int num,int c1,int c2,int s) //找到可以一次運走的所有狀態
{
if(num == n)
{
sta[cnt++]=s;
return ;
}
if(c1>=a[num])
dfs(num+1,c1-a[num],c2,s|(1<<num));
if(c2>=a[num])
dfs(num+1,c1,c2-a[num],s|(1<<num));
dfs(num+1,c1,c2,s);
}*/
int main()
{
int i,j,k,m,T,newsta;
int fg=1;
scanf("%d",&T);
while(T--)
{
memset(d,0x3f,sizeof(d));
scanf("%d%d%d",&n,&c1,&c2);
if(c1>c2) swap(c1,c2);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
cnt=0;
//printf("%d\n",judge(0));
for(i=0;i<(1<<n);i++) //way2
if(judge(i))
sta[cnt++]=i;
// for(i=(1<<n)-1;i>0;i--) dp[i]=INF; 求結果way1 有dp的感覺
//36 dp[0]=0;
//37 for(i=0;i<s;i++)
//38 for(j=(1<<n)-1-g[i];j>=0;j--)//因爲g[i]可以一次運走
//39 if(!(j&g[i])) dp[j|g[i]]=min(dp[j|g[i]],dp[j]+1);
//40 printf("Scenario #%d:\n%d\n\n",cs++,dp[(1<<n)-1]);
//dfs(0,c1,c2,0); //way1
d[0]=0;
for(i=0;i<(1<<n)-1;i++) //求結果way2 //有點難理解。。。有點想廣搜的思想。。這是這與先取誰無關,而且每個步驟沒有重複元素,因此該方法方法可行而且更快。其實有些轉態枚舉沒用,每一次枚舉是在上一次枚舉的所有狀態下再根據此,在已得到的狀態加上可行的狀態,期間可能會有許多不是最優狀態,但是最優轉態一定可以得到。狀態
{
for(j=0;j<cnt;j++)
{
if(i&(sta[j]))
continue;
newsta=i|sta[j];
d[newsta]=min(d[newsta],d[i]+1);
}
}
printf("Scenario #%d:\n%d\n\n",fg++,d[(1<<n)-1]);
}
return 0;
}
code 2
#include <iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define positive(a) ((a)>0?(a):-(a))
using namespace std;
int dp[1<<10];//dp[i]表示物品到i這個狀態需要的最少趟數
int state[1<<10];//記錄兩輛車一趟可以運的狀態
int n,cnt,w[15],can1,can2;//can1,can2兩輛車的容量
void dfs(int num,int c1,int c2,int s)//用dfs搜出兩輛車一趟可運出的狀態
{ //num表示決策到第num個物品了。c1
//表示第一輛車還剩的容量。c2表示第二
//輛車還剩容量s表示決策後的狀態
if(num>=n)//如果物品全部決策完
{
if(!dp[s])//狀態第一次出現記錄狀態。並標記
{
state[cnt++]=s;
dp[s]=1;
}
return;
}
if(w[num]<=c1)//如果第一輛車能裝
dfs(num+1,c1-w[num],c2,s|(1<<num));
if(w[num]<=c2)//如果第二輛車能裝
dfs(num+1,c1,c2-w[num],s|(1<<num));
dfs(num+1,c1,c2,s);//兩車都不裝
}
int main()
{
int t,cas,i,j,news;
scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
scanf("%d%d%d",&n,&can1,&can2);
for(i=0;i<n;i++)
scanf("%d",w+i);
cnt=0;
memset(dp,0,sizeof dp);
dfs(0,can1,can2,0);
memset(dp,0x3f,sizeof dp);
dp[0]=0;
for(i=0;i<(1<<n);i++)//枚舉狀態
{
for(j=0;j<cnt;j++)
{
if(i&state[j])//物品只能裝一次。排除衝突
continue;
news=i|state[j];//新狀態
dp[news]=MIN(dp[news],dp[i]+1);
}
}
printf("Scenario #%d:\n%d\n\n",cas,dp[(1<<n)-1]);
}
return 0;
}