题目链接: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;
}