[noip2017]宝藏

https://www.zybuluo.com/ysner/note/1286437

题面

戳我

解析

这确实是一道搜索可以过的题。。。
一般的暴力,就是枚举出发点,记录下当前已到过哪些点,以及每个点的深度(\(K\)值),然后对每个状态枚举走的下一条路的起点和终点。
当然,最优性剪枝谁都会。

这样复杂度\(O(3^nn^3)\)。可以获得\(70pts\)

然而我们可以再加剪枝。
对于出发点相同的同一状态,如果当前到达当前状态的费用比以前的大,就可以停止搜索。
这样就可以\(AC\)。。。

显然这个剪枝是有问题的,因为后面的结果还受当前状态中每个点的深度的影响。也可能当前不优,最后能最优呢。

给一组数据\(Hack\)一下。(来自洛谷讨论版
正确答案\(1043\)。我的代码去掉剪枝是\(1043\),加上剪枝\(1046\)
我原来的\(AC\)代码

il void upd(re int &x,re int y){x=x<y?x:y;}
il void dfs(re int tag,re int sum)
{
  if(sum>=ans) return;
  if(tag==(1<<n)-1) {upd(ans,sum);return;}
  fp(s,1,n)
    if(d[s])
   fp(t,1,n)
     if(!d[t]&&mp[s][t]!=inf)
     {
       re int nxt=tag|(1<<t-1),res=sum+d[s]*mp[s][t];
     if(f[nxt]>res)//剪枝
       {
     d[t]=d[s]+1;
     f[nxt]=res;
     dfs(nxt,res);
     d[t]=0;
       }
     }
}
int main()
{
  n=gi();m=gi();
  fp(i,0,19) fp(j,0,19) mp[i][j]=inf;
  fp(i,1,m)
    {
      re int u=gi(),v=gi(),w=gi();
      mp[u][v]=mp[v][u]=min(mp[u][v],w);
    }
  fp(i,1,n)
    {
      memset(d,0,sizeof(d));memset(f,63,sizeof(f));
      d[i]=1;f[1<<i-1]=0;
      dfs(1<<i-1,0);
    }
  printf("%d\n",ans);
  return 0;
}

下面来谈一谈有理有据的算法。
用二进制表示结点集合,设\(f[i][j]\)表示点\(i\)在当时可走点的集合为\(j\)的情况下,下一步走的距离最小是多少。
此时点深度也是确定的,所以也能保证答案最小。

再设\(dp[i][j]\)表示当前加入深度为\(i\)的点,此时已加入点集合为\(j\)的最小代价。
然后按照深度依次更新\(dp\)值,就没有后效性的问题了。
具体来说,是先枚举深度,再枚举加完点后的集合,再枚举其子集(即加点前的集合)。最后在加点前的集合中枚举出发点,以统计\(f\)的和。

复杂度\(O(3^nn^2)\)
所以这题应该放\(T3\)的。。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define re register
#define il inline
#define db double
#define fp(i,a,b) for(re int i=a;i<=b;++i)
#define fq(i,a,b) for(re int i=a;i>=b;--i)
using namespace std;
const int inf=5e6;
int n,m,f[13][1<<12],dis[13][13],g[13][1<<12],all;
il ll gi()
{
  re ll x=0,t=1;
  re char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
il int max(re int x,re int y){return x>y?x:y;}
int main()
{
  n=gi();m=gi();all=(1<<n)-1;
  fp(i,1,12) fp(j,1,12) dis[i][j]=inf;
  fp(i,1,12) fp(j,0,all) f[i][j]=g[i][j]=inf;
  fp(i,1,m)
    {
      re int u=gi(),v=gi(),w=gi();
      dis[u][v]=dis[v][u]=min(dis[u][v],w);
    }
  fp(i,1,n) dis[i][i]=0;
  fp(S,0,all)
    fp(i,1,n)
    if((1<<i-1)&S)
      fp(j,1,n)
	if(!((1<<j-1)&S))
	  g[j][S]=min(g[j][S],dis[i][j]);
  fp(i,1,n) f[1][1<<i-1]=0;
  fp(i,2,n)
    fp(S,0,all)
    for(re int P=S;P;P=(P-1)&S)
      {
	re int tot=0;
	fp(j,1,n) if((1<<j-1)&(P^S)) tot+=g[j][P];
	f[i][S]=min(f[i][S],f[i-1][P]+(i-1)*tot);
      }
  printf("%d\n",f[n][all]);
  return 0;
}

posted @ 2018-09-18 20:14  小蒟蒻ysn  阅读(207)  评论(0编辑  收藏  举报