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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值