HDU 4281 Judges' response(状态压缩DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4281

题意:有n个点,编号0到n-1。每两个点之间有距离,每个点(除0外)遍历时有花费。在0点有好多人,每个人有m的时间去遍历。

第一问:问遍历完1到n-1用的最少人数?(不用考虑点与点之间的距离)

第二问:问怎么安排每个人遍历哪些点可以使得所有人走的路程之和最小?(所有人从0出发最后要回到0点)

 

思路:

第一问直接状态压缩dp[state][pos]表示state状态下到达pos需要的最少人数?

第二问也是状态压缩,f[state][pos]表示state状态下到达pos的最少路程。

用几个子集组成大的集合的时候,是这样写的:

k=(1<<n)-2;

for(i=1;i<(1<<n);i++,k--) if(ans[i]<INF)  for(j=k;j;j=(j-1)&k)

      ans[i|j]=min(ans[i|j],ans[i]+ans[j]);

 

 

#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <cstdio>
#define INF 0x3f3f3f3f
#define P(x) ((x)*(x))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;


int dp[(1<<16)+5][2];
int f[(1<<16)+5][20],dis[20][20],n,m,ans[(1<<16)+5],c[20];
int visit[(1<<16)+5][20],cost[(1<<16)+5];


void DP1()
{
    int i,j,k,visit[(1<<16)+5],x,y;
    queue<int> Q;

    memset(visit,0,sizeof(visit));
    memset(dp,-1,sizeof(dp));
    dp[1][0]=1;
    dp[1][1]=m;
    Q.push(1);
    visit[1]=1;
    while(!Q.empty())
    {
        k=Q.front();
        Q.pop();

        visit[k]=0;
        for(i=0;i<n;i++) if(!(k&(1<<i)))
        {
            j=k|(1<<i);
            if(dp[k][1]>=c[i]) x=dp[k][0],y=dp[k][1]-c[i];
            else x=dp[k][0]+1,y=m-c[i];
            if(dp[j][0]<0||dp[j][0]>x||dp[j][0]==x&&dp[j][1]<y)
            {
                dp[j][0]=x;
                dp[j][1]=y;
                if(!visit[j]) Q.push(j),visit[j]=1;
            }
        }
    }
    printf("%d ",dp[(1<<n)-1][0]);
}


void DP2()
{
    int i,j,k,t,state,pos;
    queue<int> Q;

    for(i=0;i<(1<<n);i++)
    {
        cost[i]=0;
        for(j=0;j<n;j++) if(i&(1<<j)) cost[i]+=c[j];
    }

    memset(visit,0,sizeof(visit));
    memset(f,-1,sizeof(f));
    f[0][0]=0;
    visit[0][0]=1;
    Q.push(0);

    while(!Q.empty())
    {
        k=Q.front();
        Q.pop();

        state=k%100000;
        pos=k/100000;
        visit[state][pos]=0;
        for(i=0;i<n;i++) if(!(state&(1<<i)))
        {
            k=state|(1<<i);
            t=f[state][pos]+dis[pos][i];
            if(cost[k]>m) continue;
            if(f[k][i]<0||f[k][i]>t)
            {
                f[k][i]=t;
                if(!visit[k][i])
                {
                    visit[k][i]=1;
                    Q.push(i*100000+k);
                }
            }
        }
    }

    memset(ans,INF,sizeof(ans));
    for(i=0;i<(1<<n);i++)  for(j=0;j<n;j++)
    {
        if(f[i][j]<0||!(i&(1<<j))||f[i][j]==INF) continue;
        ans[i]=min(ans[i],f[i][j]+dis[0][j]);
    }
    k=(1<<n)-2;
    for(i=1;i<(1<<n);i++,k--) if(ans[i]<INF) for(j=k;j;j=(j-1)&k)
        ans[i+j]=min(ans[i+j],ans[i]+ans[j]);
    printf("%d\n",ans[(1<<n)-1]);
}

int main()
{
    while(scanf("%d%d",&n,&m)!=-1)
    {
        int X[20],Y[20],i,j;
        for(i=0;i<n;i++) scanf("%d%d",&X[i],&Y[i]);
        for(i=0;i<n;i++) scanf("%d",&c[i]);
        for(i=0;i<n;i++) if(m<c[i]) break;
        if(i<n) {puts("-1 -1");continue;}
        for(i=0;i<n;i++) for(j=0;j<n;j++)
        {
            if(i==j) dis[i][j]=0;
            else dis[i][j]=ceil(sqrt(1.0*P(X[i]-X[j])+1.0*P(Y[i]-Y[j])));
        }
        DP1();
        DP2();
    }
    return 0;
}

 

  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值