题目链接
4 0 3 8 6 4 0 7 4 7 5 0 2 6 9 3 0 30 8 30 4 0 2 3 3 2 0 3 3 2 3 0 3 2 3 3 0 2 3 3
36 -1HintExplanation: In case #1: The Super Doge travels to Doge Planet 2 at the time of 8 and to Doge Planet 3 at the time of 12, then to Doge Planet 4 at the time of 16. The minimum sum of all arrival time is 36.
题意:n个点的有向图,从第一个点开始要访问完所有点,起点时间为0,每个点都有访问的时间限制,超过限制则不能访问,设访问第i个点的时间为 Ti。求所有Ti的和最小为多少?
题解:容易想到,先用floyd求出两点间的最短路,剩下的问题就是确定一个访问顺序,让其满足条件并且访问每个点的时间和最小。暴力的复杂度为n!,即30!,肯定是不行的,但是本问题容易进行剪枝,所以我们可以通过强剪枝降低复杂度。
具体而言有两种剪枝:
1,可行性剪枝。假设访问当前结点的时间超过截止时间,则减掉。对于当前没有访问的结点,假设下一次直接访问该点依然超过时间限制,说明当前状态一定无法得出可行解,减掉。
2,最优性剪枝。假设当前花费的总时间超过已知最优答案,则减掉。对于没有访问过的点,我们假设它的访问时间为从当前结点直接到该结点,如果总时间依然大于或等于已知最优解,则减掉。
另外,我还将结点按截至时间排序,目的是为了让搜索过程中尽快收到可行解,从而进行最优性剪枝。
代码如下:
#include<cstdio> #include<string> #include<cstring> #include<iostream> #include<stdio.h> #include<map> #include<algorithm> #include<string> #define inff 0x3fffffff using namespace std; typedef unsigned __int64 ULL; int n; int tu[40][40]; bool use[40]; int ans; struct node { int val,id; }de[40]; void dfs(int id,int nu,int ti,int zs) { if(nu==n) { ans=zs; return ; } use[id]=true; bool ok=true; int ix=zs; int i; for(i=2;i<=n;i++) { if(!use[de[i].id]) { if(ti+tu[id][de[i].id]>de[i].val) ok=false; ix+=ti+tu[id][de[i].id]; } } if(!ok||ix>=ans) { use[id]=false; return ; } for(i=2;i<=n;i++) { if(!use[de[i].id]) { ix=ti+tu[id][de[i].id]; if(ix<=de[i].val&&zs+ix<ans) dfs(de[i].id,nu+1,ix,zs+ix); } } use[id]=false; } bool cmp(node xx,node yy) { return tu[1][xx.id]<tu[1][yy.id]; } int main() { int i,j,k; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { scanf("%d",&tu[i][j]); } } for(k=1;k<=n;k++) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { tu[i][j]=min(tu[i][j],tu[i][k]+tu[k][j]); } } } de[1].val=0; de[1].id=1; for(i=2;i<=n;i++) { scanf("%d",&de[i].val); de[i].id=i; } sort(de+2,de+n+1,cmp); memset(use,false,sizeof(use)); ans=inff; use[1]=true; dfs(1,1,0,0); if(ans==inff) ans=-1; printf("%d\n",ans); } return 0; }