zoukankan      html  css  js  c++  java
  • Jzoj3907 蜀传之单刀赴会(梦回三国系列)

    【题目背景】
    公元215年,刘备取益州,孙权令诸葛瑾找刘备索要荆州。刘备不答应,孙权极为恼恨,便派吕蒙率军取长沙、零陵、桂阳三郡。长沙、桂阳蜀将当即投降。刘备得知后,亲自从成都赶到公安(今湖北公安),派大将关羽争夺三郡。孙权也随即进驻陆口,派鲁肃屯兵益阳,抵挡关羽。双方剑拔弩张,孙刘联盟面临破裂,在这紧要关头,鲁肃为了维护孙刘联盟,不给曹操可乘之机,决定当面和关羽商谈。“肃邀羽相见,各驻兵马百步上,但诸将军单刀俱会”。双方经过会谈,缓和了紧张局势。随后,孙权与刘备商定平分荆州,“割湘水为界,于是罢军”,孙刘联盟因此能继续维持。
     
    【问题描述】

    关羽受鲁肃邀请,为了大局,他决定冒险赴会。他带着侍从周仓,义子关平,骑着赤兔马,手持青龙偃月刀,从军营出发了,这就是历史上赫赫有名的“单刀赴会”。关羽平时因为军务繁重,决定在这次出行中拜访几个多日不见的好朋友。然而局势紧张,这次出行要在限定时间内完成,关公希望你能够帮助他安排一下行程,安排一种出行方式,使得从军营出发,到达鲁肃处赴会再回来,同时拜访到尽可能多的朋友,在满足这些条件下行程最短。注意拜访朋友可以在赴会之前,也可以在赴会之后。现在给出地图,请你完成接下来的任务。


    我们发现k非常的小,这种问题是经典的状压DP,又因为是无环图,所以可以用dijk先算出每个关键点到其他点的距离,再调用TSP问题的方法即可

    (这里说一下:dijk+heap很好写,信不信比你的spfa还短,而且可以在负权图上面跑!但是效率会下降很多)

    #pragma GCC opitmize("O3")
    #pragma G++ opitmize("O3")
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    struct Node{ int d,id; } x;
    struct Edge{ int v,c,nt; } G[100010];
    int h[10010],d[20][10010],d2[20][20];
    int n,m,k,t,cnt=0,w[20],f[20][1<<18];
    inline bool gmin(int& a,int b){ return a>b?(a=b)|1:0; }
    inline bool operator < (Node a,Node b){ return a.d>b.d; }
    inline int tot(int s,int c=0){ for(;s;s^=s&-s) ++c; return c; }
    inline void adj(int x,int y,int c){ G[++cnt]=(Edge){y,c,h[x]}; h[x]=cnt; }
    void dijk(int* d,int s){
    	priority_queue<Node> q;
    	d[s]=0; q.push((Node){0,s});
    	for(int u;!q.empty();){
    		x=q.top(); q.pop();
    		if(d[u=x.id]<x.d) continue;
    		for(int v,i=h[u];i;i=G[i].nt)
    			if(gmin(d[v=G[i].v],d[u]+G[i].c)) q.push((Node){d[v],v});
    	}
    }
    int main(){
    	scanf("%d%d%d%d",&n,&m,&k,&t);
    	for(int x,y,c;m--;){
    		scanf("%d%d%d",&x,&y,&c);
    		adj(x,y,c); adj(y,x,c);
    	}
    	memset(d,63,sizeof d);
    	memset(f,63,sizeof f);
    	w[0]=1; w[++k]=n;
    	dijk(d[0],1); dijk(d[k],n);
    	for(int i=1;i<k;++i){
    		scanf("%d",w+i);
    		dijk(d[i],w[i]);
    		for(int j=0;j<i;++j) d2[i][j]=d2[j][i]=d[i][w[j]];
    	}
    	for(int j=0;j<k;++j){
    		d2[k][j]=d2[j][k]=d[k][w[j]];
    		f[j][1<<j]=d2[0][j];
    	}
    	for(int S=0;S<(1<<k+1);++S)
    		for(int i=0;i<=k;++i)
    			if(S&(1<<i))
    				for(int j=0;j<=k;++j)
    					if(i!=j&&(S&(1<<j)))
    						f[i][S]=min(f[i][S],f[j][S-(1<<i)]+d2[j][i]);
    	int Mx=-1,Ml=0;
    	for(int S=1<<k;S<(1<<k+1);++S)
    		if((S&(1<<k))&&(S&1)){
    			int c=tot(S)-2,v=1<<30;
    			if(c>=Mx){
    				for(int j=0;j<=k;++j)
    					v=min(v,f[j][S]+d2[0][j]);
    				if(v<=t){
    					if(Mx==c) gmin(Ml,v);
    					else Ml=v; Mx=c;
    				}
    			}
    		}
    	if(~Mx) printf("%d %d",Mx,Ml); else puts("-1");
    }

  • 相关阅读:
    搭建GitLab+Jenkins持续集成环境图文教程
    Linux学习教程,Linux入门教程(超详细)
    Python基础教程,Python入门教程(非常详细)
    我的Dojo中有一个Mojo(如何编写Maven插件)
    Nginx与安全有关的几个配置
    Tomcat安全设置
    MFC 重绘CButton 支持透明背景的png
    Qt中 QTreeView、QTableView单元项进行重命名
    C++将int与size_t进行比较的陷阱
    C++ using的用法
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/7774391.html
Copyright © 2011-2022 走看看