HDU 4892 状压dp

Defence of the Trees

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 224    Accepted Submission(s): 51
Special Judge


Problem Description
There are n trees (each of them can be considered as a point) on a two-dimensional coordinate system. There are k categories of trees, category of the i th tree are represented by A i. There are also m stumps (each of them can be considered as a point, too) on this two-dimensional coordinate system. Stumps can be connected with straight wires.

Your task is to connect some of the stumps by wires to build exactly one simple polygon (that is, a polygon without self-intersection and degeneration) which contains trees of all k categories inside. Meanwhile, in order to save money, you should make the total length of wires as short as possible. Your task is to calculate the shortest total length of wires, or to determine that there is no solutions.
 

Input
There are several testcases, please process till EOF.

In each testcases:
First one line, three numbers n, m, k. Following n lines contains n 2D-coordinates describing each tree. Next one line contains n number, where i th number A i denotes i th tree's category. Next m lines describe the coordinates of stumps, you may assume that there is no trees lying on segments between any two stumps.

All input will be integer, and absolute value of them will be less than 23333, 1 ≤ n ≤ 300, 1 ≤ m ≤ 40, 1 ≤ k ≤ 6,1 ≤ A i ≤ k.In about 90% of testcases,m ≤ 10.
 

Output
For each testcase, print one line, if there is no solutions, Output "Impossible" (without quotes), else output a real number indicating the minimal total length. Your answer will be considered correct if and only if the absolute error or relative error are less than 10 -6.
 

Sample Input
  
  
2 4 1 0 0 2 0 1 1 1 -1 1 1 -3 -1 5 1 2 4 2 0 0 2 0 1 2 1 -1 1 1 -3 -1 5 1
 

Sample Output
  
  
10.472135955000 16.944271909999
 

Author
Fudan University

题意:

有n颗树,总共有k种种类,每棵树有一个种类,有m个木桩,要选一些木桩连起来,使得包含所有的树的种类,并且连线最短。

由于k<=6,数据很小,一看就是状压dp, 把m个点的三角形全部找出来,然后把三角形内部k个种类的状态全部找出来,然后就是三角形合并,每次合并都会是

两边的周长减去公共边的长度的2倍,然后状态合并,

用类似与最短路的方法求答案。

搞了半天,发现spfa有问题,我们需要记录k的状态,还需要记录当前周长的状态,由于一个状态可能有多个周长,用数组记录也不大方便,所以把它拧成一个节点,丢进优先队列里,这样合并就没问题了,

代码:

/* ***********************************************
Author :rabbit
Created Time :2014/8/15 10:13:28
File Name :5.cpp
************************************************ */
#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <string>
#include <time.h>
#include <math.h>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define INF 1e15
#define eps 1e-9
#define pi acos(-1.0)
typedef long long ll;
int dcmp(double x){
	if(fabs(x)<eps)return 0;
	return x>0?1:-1;
}
struct Point{
	double x,y;
	Point(double _x=0,double _y=0){
		x=_x;
		y=_y;
	}
};
Point operator + (Point a,Point b){
	return Point(a.x+b.x,a.y+b.y);
}
Point operator - (Point a,Point b){
	return Point(a.x-b.x,a.y-b.y);
}
double dist(Point a,Point b){
	double ss=a.x-b.x;
	double tt=a.y-b.y;
	return sqrt(ss*ss+tt*tt);
}
double Cross(Point a,Point b){
	return a.x*b.y-a.y*b.x;
}
int tot,id[40][40][40],state[11010];
int getid(int a,int b,int c){
	if(b>c)swap(b,c);
	if(a>b)swap(a,b);
	if(b>c)swap(b,c);
	return id[a][b][c];
}
int head[11010],tol;
struct Edge{
	int next,to;
	double val;
}edge[2002000];
void addedge(int u,int v,double val){
	edge[tol].to=v;
	edge[tol].val=val;
	edge[tol].next=head[u];
	head[u]=tol++;
}
double dp[11010][1<<6];
int vis[10010][1<<6];
Point p1[400],p2[400];
int dd[500];
double A[100100];
bool check(Point a,Point b,Point c,Point d){
	double t1=fabs(Cross(a-d,b-d));
	double t2=fabs(Cross(a-d,c-d));
	double t3=fabs(Cross(b-d,c-d));
	double t4=fabs(Cross(b-a,c-a));
	if(dcmp(t4-t3-t2-t1)==0)return true;
	return false;
}
int main()
{
     //freopen("data.in","r",stdin);
     //freopen("data.out","w",stdout);
     int N,M,K;
	 while(cin>>N>>M>>K){
		 memset(head,-1,sizeof(head));tol=0;
		 tot=0;
		 for(int i=1;i<=N;i++)cin>>p1[i].x>>p1[i].y;
		 for(int i=1;i<=N;i++)cin>>dd[i],dd[i]--;
		 for(int i=1;i<=M;i++)cin>>p2[i].x>>p2[i].y;
		 for(int i=1;i<=M;i++)
			 for(int j=i+1;j<=M;j++)
				 for(int k=j+1;k<=M;k++)
					 id[i][j][k]=++tot;
	//	 cout<<"tot="<<tot<<endl;
		 memset(state,0,sizeof(state));
		 for(int i=1;i<=M;i++)
			 for(int j=i+1;j<=M;j++)
				 for(int k=j+1;k<=M;k++)
					 for(int a=1;a<=N;a++)
						 if(check(p2[i],p2[j],p2[k],p1[a]))state[id[i][j][k]]|=(1<<dd[a]);
		 //cout<<"state: ";for(int i=1;i<=tot;i++)cout<<state[i]<<" ";cout<<endl;
		 for(int i=1;i<=M;i++)
			 for(int j=i+1;j<=M;j++)
				 for(int k=j+1;k<=M;k++){
					 double val=dist(p2[i],p2[j])+dist(p2[i],p2[k])+dist(p2[j],p2[k]);
					 addedge(0,id[i][j][k],0);
					 A[id[i][j][k]]=val;
				 }
		 for(int i=1;i<=M;i++)
			 for(int j=i+1;j<=M;j++)
				 for(int k=j+1;k<=M;k++)
					 for(int t=1;t<=M;t++){
						 if(t==i||t==j||t==k)continue;
						 int s1=id[i][j][k],s2;
						 double t1,t2;
						 t1=Cross(p2[j]-p2[i],p2[k]-p2[i]);t2=Cross(p2[j]-p2[i],p2[t]-p2[i]);//i,j共边。
						 if(dcmp(t1)*dcmp(t2)<0)addedge(s1,getid(i,j,t),-2*dist(p2[i],p2[j]));

						 t1=Cross(p2[k]-p2[i],p2[t]-p2[i]);t2=Cross(p2[k]-p2[i],p2[j]-p2[i]);//i,k共边
						 if(dcmp(t1)*dcmp(t2)<0)addedge(s1,getid(i,k,t),-2*dist(p2[i],p2[k]));

						 t1=Cross(p2[k]-p2[j],p2[t]-p2[j]);t2=Cross(p2[k]-p2[j],p2[i]-p2[j]);//j,k共边
						 if(dcmp(t1)*dcmp(t2)<0)addedge(s1,getid(j,k,t),-2*dist(p2[j],p2[k]));
					 }
		 //cout<<"edge: "; for(int i=0;i<tol;i++)cout<<edge[i].val<<endl;
		 for(int i=0;i<=tot;i++)
			 for(int j=0;j<(1<<K);j++)
				 dp[i][j]=INF;
		
		 if(ans<INF)printf("%.12lf\n",ans);
		 else puts("Impossible");
	 }
	 //Point a,b,c,d;while(cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>d.x>>d.y)cout<<check(a,b,c,d)<<endl;
     return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值