bnu Treasure Diving
http://www.bnuoj.com/contest/problem_show.php?pid=26305
题意:给一个无向图,现选定图中的k(k<=8)个点,如果最多能走tot米,问最多能经过几个点(要求回到原点)。
思路:
暴力出k个点之间的最短距离,剩下k个点还是暴力弄。
但这样还是超时了,注意到tot最多只是1000000,因为要回到原点,所以距离大于tot的肯定不满足。

#include<stdio.h> #include<string.h> #include<iostream> #include<vector> #include<queue> #include<algorithm> using namespace std; const int maxn = 10005; const int inf = 100000000; struct nd{ int v,next,c; }as[maxn*10]; int ecnt,head[maxn]; int dis[maxn],vis[maxn]; int lis[12]; int n,m,tot,k; int cs[12][12]; int bj[12]; int mx,ct,f; void add(int a,int b,int c) { as[ecnt].v = b; as[ecnt].c = c; as[ecnt].next = head[a]; head[a] = ecnt++; } void spfa(int s,int idx) { int i,u,v,c; for(i = 0; i < n; ++ i) dis[i]=inf,vis[i]=0; queue<int>que; que.push(s); dis[s]=0; while(!que.empty()){ u = que.front(); que.pop(); vis[u]=0; for(i = head[u]; i!=-1; i=as[i].next){ v = as[i].v; c = as[i].c; if(dis[u]+c>=tot)continue;//这个剪枝很重要 if(dis[v]>dis[u]+c){ dis[v]=dis[u]+c; if(!vis[v]){ que.push(v); vis[v]=1; } } } } for(i = 0; i < k; ++ i){ if(cs[idx][i]>dis[lis[i]]) cs[idx][i]=dis[lis[i]]; } } void readin() { int i,j; scanf("%d %d",&n,&m); for(i=0;i<n;++i)head[i]=-1; ecnt = 0; for(i = 0; i < m; ++ i){ int a,b,c; scanf("%d %d %d",&a,&b,&c); add(a,b,c); add(b,a,c); } scanf("%d",&k); for(i = 0; i < k; ++ i) scanf("%d",lis+i); scanf("%d",&tot); if(!k)return; sort(lis,lis+k); f = 0; if(lis[0])lis[k++]=0; else f = 1; sort(lis,lis+k); for(i = 0; i < 12; ++ i) for(j = 0; j < 12; ++ j) if(i^j)cs[i][j]=inf; else cs[i][j]=0; for(i = 0; i < k; ++ i) spfa(lis[i],i); } void ts(int kk,int tt,int ss) { if(mx<tt) mx=tt; if(tt==(k-1)||ct) { ct=1;return;} for(int i=1;i<k;i++) if(bj[i]==0&&ss>=(cs[0][i]+cs[kk][i])) { bj[i]=1; ts(i,tt+1,ss-cs[kk][i]); bj[i]=0; } } int main() { int t; scanf("%d",&t); while(t--){ readin(); mx=0;ct=0; if(k){ memset(bj,0,sizeof(bj)); ts(0,0,tot); } printf("%d\n",mx+f); } return 0; }