BZOJ 4479: [Jsoi2013]吃货jyy

一句话题意:求必须包含某K条边的回路(回到1),使得总权值最小

转化为权值最小的联通的偶点

令F[i]表示联通状态为i的最小权值,(3^n状压)表示不在联通块内/奇点/偶点,连边时先不考虑必选的边的度数和权值

最后加上必须的边(保证必须的边都被选了)

连完这些边以后考虑剩下的一些奇点,两两配对,G[i]表示状态为i的奇点两两配对的代价(2^n状压)

#include<cstdio>
#include<algorithm>
using namespace std;
int cnt,n,k,last[15],dis[15][15],G[10005],F[2000005],vis[15],a[15],mi[15];
struct node{
	int to,next;
}e[1000005];
void add(int a,int b){
	e[++cnt].to=b;
	e[cnt].next=last[a];
	last[a]=cnt;
}
int main(){
	scanf("%d%d",&n,&k);
	for (int i=0; i<n; i++)
		for (int j=0; j<n; j++)
			dis[i][j]=1e9;
	int ans=0;
	for (int i=1; i<=k; i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		x--,y--;
		add(x,y);
		add(y,x);
		ans+=z;
		vis[x]++;
		vis[y]++;
		dis[x][y]=min(dis[x][y],z);
		dis[y][x]=min(dis[y][x],z);
	}
	int m;
	scanf("%d",&m);
	for (int i=1; i<=m; i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		x--,y--;
		dis[x][y]=min(dis[x][y],z);
		dis[y][x]=min(dis[y][x],z);
	}
	for (int k=0; k<n; k++)
		for (int i=0; i<n; i++)
			for (int j=0; j<n; j++)
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	for (int i=0; i<(1<<n); i++) G[i]=1e9;
	G[0]=0;
	for (int i=0; i<(1<<n); i++)
		for (int x=0; x<n; x++)
			if (!(i&(1<<x))){
				for (int y=x+1; y<n; y++)
					if (!(i&(1<<y))) 
						G[i|(1<<x)|(1<<y)]=min(G[i|(1<<x)|(1<<y)],G[i]+dis[x][y]);
			}
	mi[0]=1;
	for (int i=1; i<=n; i++) mi[i]=mi[i-1]*3;
	for (int i=0; i<mi[n]; i++) F[i]=1e9;
	F[2]=0;
	for (int now=2; now<mi[n]; now++)
		if (F[now]!=1e9){
			int N=0;
			for (int i=0; i<n; i++) if (now/mi[i]%3) a[N++]=i;
			for (int i=0; i<n; i++)
				if (!(now/mi[i]%3)){
					for (int j=last[i]; j; j=e[j].next){
						int V=e[j].to;
						if (now/mi[V]%3){
							int To=now+mi[i]*2;
							F[To]=min(F[To],F[now]);
						}
					}
					for (int j=0; j<N; j++){
						int To=now+mi[i];
						if (now/mi[a[j]]%3==1) To+=mi[a[j]];
						else if (now/mi[a[j]]%3==2) To-=mi[a[j]];
						F[To]=min(F[To],F[now]+dis[i][a[j]]);
					}
				}
		}
	int ANS=1e9;
	for (int now=0; now<mi[n]; now++){
		int flag=0,Now=now;
		for (int i=0; i<n; i++) if (vis[i] && !(Now/mi[i]%3)){
			flag=1;
			break;
		}
		if (flag) continue;
		for (int i=0; i<n; i++) 
			if (vis[i]&1){
				if (Now/mi[i]%3==1) Now+=mi[i];
				else if (Now/mi[i]%3==2) Now-=mi[i];
			}
		int To=0;
		for (int i=0; i<n; i++) if (Now/mi[i]%3==1) To|=(1<<i);
		ANS=min(ANS,F[now]+G[To]);
	}
	printf("%d\n",ans+ANS);
	return 0;
}

  

posted @ 2018-10-30 21:08  ~Silent  阅读(232)  评论(0编辑  收藏  举报
Live2D