Relocation poj 2923

題目大意:

給你一些物品。每個物品有相應的重量。給你兩輛載重不一定相同的車。問你最少要多少趟才能把所有物品運完。

思路:

狀態壓縮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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章