题目链接
2 4 5 2 1 2 8 2 3 4 3 4 19 4 1 6 2 4 7 2 25 18 1 3 4 12 6 1 3 4 6 2 1 2 5 1 4 4 3 1 1 3 2 1 3 4 1 2 4 10 2 8 3 1 4 4 8 3 1 2
Case #1: 53 Case #2: 14
题意:N个点,M条双向边,K个景点,告诉每个景点所在的位置,一个点可能有多个景点。要游玩第i个景点,要花 Ti 的时间排队,但是在某些点可以买第 i 个景点的快速通道的票,如果买了快速通道的票,只用花 PTi 的时间排队。通过一条边,要花费相应的时间。买票不花时间。问从第1个点开始,游玩了K个景点后,又回到第1个点的最短时间。
N<=50 , k<=8
题解:因为K很小,所以显然状压dp可做。用dp[ i ][ j ][ k ],表示在第 i 个点,已经访问过的景点的状态为 j ,已有的票的状态为 k ,的状态下的最短时间。用floyd处理出两点间的最短距离。我是这样转移的,下一步要么去访问景点,要么去买票。那么可以把一个状态看成一个点,就是求个最短路。最开始我写了发堆优化的dij,T了。仔细分析,一个点最多可以转移出400个状态,边数最坏是状态数的400倍,远大于nlgn(n表示状态数),为稠密图,T是很正常的。换成了spfa就A了。后来我又贪心的优化了下,如果到了一个点一定要把该点所有的票买完,跑了600+ms。
看看了别人的题解,转移还可以写成,到了一个点一定把该点所有的景点访问完,并把该点所有的票买完,这样不会影响最后结果的最优性。一定要把票买完很好理解,贪心就是了。而对于一定要把景点访问完,也可以贪心的想,当票的状态固定的时候,我已经到了这个点,那么我访问完该点所有的景点一定更优,因为票的状态已经固定,在走到其它点再走回该点只能让时间增加,没有意义。
这题还可以用三进制状压做,分别表示访问过,有票没访问过,没票没访问过。这种方法状态数更少,应该更优。
我的代码如下:
//#pragma comment(linker, "/STACK:102400000,102400000") #include<stdio.h> #include<queue> #include<iostream> #include<algorithm> #include<string.h> #include<string> #include<math.h> #include<stack> #define nn 55 #define mod 1000 #define inff 0x3fffffff typedef __int64 LL; using namespace std; int n,m,K; int tu[nn][nn]; int p[nn],T[nn],pt[nn]; int ve[10][nn]; int len[10]; int dp[nn][(1<<8)][(1<<8)]; bool inque[nn][(1<<8)][(1<<8)]; vector<int>vv[nn]; struct node { int id,jing,piao,val; friend bool operator<(node xx,node yy) { return xx.val>yy.val; } node(){} node(int x,int y,int z,int g) { id=x,jing=y,piao=z,val=g; } }sta; //priority_queue<node>que; queue<node>que; int ans; //void gengxin(int id,int jing,int piao,int val) //{ // if(dp[id][jing][piao]>val) // { // dp[id][jing][piao]=val; // que.push(node(id,jing,piao,val)); // } //} void gengxin(int id,int jing,int piao,int val) { if(dp[id][jing][piao]>val) { dp[id][jing][piao]=val; if(!inque[id][jing][piao]) { inque[id][jing][piao]=true; que.push(node(id,jing,piao,val)); } } } void solve() { ans=inff; int i,j,k; for(i=1;i<=n;i++) { for(j=0;j<(1<<K);j++) { for(k=0;k<(1<<K);k++) { inque[i][j][k]=false; dp[i][j][k]=inff; } } } dp[1][0][0]=0; que.push(node(1,0,0,0)); int ix,piao; while(que.size()) { sta=que.front(); que.pop(); inque[sta.id][sta.jing][sta.piao]=false; // if(sta.val>dp[sta.id][sta.jing][sta.piao]) //continue; if(sta.jing==(1<<K)-1) { ans=min(ans,dp[sta.id][sta.jing][sta.piao]+tu[1][sta.id]); continue; } piao=sta.piao; for(i=0;i<(int)vv[sta.id].size();i++) { ix=vv[sta.id][i]; if((1<<ix)&piao) continue; piao+=(1<<ix); } if(piao>sta.piao) { gengxin(sta.id,sta.jing,piao,dp[sta.id][sta.jing][sta.piao]); continue; } for(i=0;i<K;i++) { if((1<<i)&sta.jing) continue; ix=tu[p[i]][sta.id]+dp[sta.id][sta.jing][sta.piao]; if((1<<i)&sta.piao) ix+=pt[i]; else ix+=T[i]; gengxin(p[i],sta.jing+(1<<i),sta.piao,ix); if((1<<i)&sta.piao) continue; for(j=0;j<len[i];j++) { ix=dp[sta.id][sta.jing][sta.piao]+tu[sta.id][ve[i][j]]; gengxin(ve[i][j],sta.jing,sta.piao+(1<<i),ix); } } } } int main() { int i,j,k; int t,u,v,l; int cas=1; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&K); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { tu[i][j]=i==j?0:inff; } } for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&l); tu[u][v]=min(tu[u][v],l); tu[v][u]=tu[u][v]; } for(k=1;k<=n;k++) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { tu[i][j]=min(tu[i][k]+tu[k][j],tu[i][j]); } } } for(i=1;i<=n;i++) vv[i].clear(); for(i=0;i<K;i++) { len[i]=0; scanf("%d%d%d",&p[i],&T[i],&pt[i]); scanf("%d",&u); while(u--) { scanf("%d",&v); ve[i][len[i]++]=v;; vv[v].push_back(i); } } solve(); printf("Case #%d: %d\n",cas++,ans); } return 0; }