HDU 4281 Judges' response(12年天津 MTSP问题)

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove 

题目:给出N个人,其中0号是裁判的位置,剩下有N-1的人提问,裁判需要去回答问题,每个人有一个val,每个裁判能拿到的val的上限为K。问题最少需要几个裁判,以及最少时间

http://acm.hdu.edu.cn/showproblem.php?pid=4281 

第一问可以状态压缩DP解决,dp[i]表示状态i需要几个裁判。注~~~贪心 是错的!!!!

第二问就是多个TSP问题,dp[i][j]表示当前位置 在i,可行状态为j的最小费用,best[i]表示对于状态i的最小费用(而且是一个完整状态,裁判都回到原点),因为之前TSP中是求的可行状态,也就是每个裁判的上限不能超过K。之后就是枚举所有状态,对于一个状态,枚举他的所有子集,见代码中的位运算部分。而且两个子集中必须要包含0号结点,因为每个裁判都是从0出发的

可以看这个博客:http://blog.csdn.net/woshi250hua/article/details/7961869


#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<set>
#define inf 1<<28
#define M 100005
#define N 55
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define MOD 1000000007
using namespace std;
struct Point{
	int x,y;
}p[20];
int n,m,val[20],path[20][20];
int dp[16][1<<16],state[1<<16],tot;
int best[1<<16],ok[1<<16];
int dist(Point p1,Point p2){
	return ceil(sqrt((double)(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));
}
void Get_dist(){
	for(int i=0;i<n;i++) for(int j=0;j<n;j++) path[i][j]=dist(p[i],p[j]);
}
void Init(){
	for(int i=0;i<n;i++)
		for(int j=0;j<(1<<n);j++)
			dp[i][j]=inf;
	for(int i=0;i<(1<<n);i++)
		best[i]=inf;
	dp[0][1]=0;
}
int check(int state){
	int sum=0;
	for(int i=0;i<n;i++)
		if(state&(1<<i))
			sum+=val[i];
	return sum<=m;
}
bool cmp(int a,int b){return a>b;}
int slove(){
	int dp[1<<16];
	for(int i=0;i<(1<<n);i++) dp[i]=inf;
	dp[0]=0;
	for(int i=0;i<tot;i++){
		for(int j=(1<<n)-1;j>=0;j--){	
			if(dp[j]==inf) continue;
			if(state[i]&j) continue;
			dp[state[i]+j]=min(dp[state[i]+j],dp[j]+1);
		}
	}
	return dp[(1<<n)-1]==inf?-1:dp[(1<<n)-1];
}
int TSP(){
	for(int i=0;i<(1<<n);i++){
		if(ok[i])
			for(int j=0;j<n;j++)
				if(i&(1<<j)){
					best[i]=min(best[i],dp[j][i]+path[j][0]);
					for(int k=0;k<n;k++)
						if(!(i&(1<<k)))
							dp[k][i|(1<<k)]=min(dp[k][i|(1<<k)],dp[j][i]+path[j][k]);
				}
	}
	for(int i=0;i<(1<<n);i++)
		if(i&1)
			for(int j=i&(i-1);j;j=i&(j-1))
				best[i]=min(best[i],best[j]+best[(i-j)|1]);
	return best[(1<<n)-1];
}

int main(){
	while(scanf("%d%d",&n,&m)!=EOF){
		tot=0;
		for(int i=0;i<n;i++) scanf("%d%d",&p[i].x,&p[i].y);
		for(int i=0;i<n;i++) scanf("%d",&val[i]);
		for(int i=0;i<(1<<n);i++) {
			ok[i]=check(i);
			if(ok[i])
				state[tot++]=i;
		}
		Get_dist();
		Init();
		int ans1=slove();
		if(ans1==-1) {printf("-1 -1\n");continue;}
		printf("%d %d\n",ans1,TSP());
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值