1. 程式人生 > >【圖論專題四】【JSOI2013】吃貨JYY

【圖論專題四】【JSOI2013】吃貨JYY

【江蘇省省選2013】吃貨JYY

【JSOI2013】吃貨JYY (Standard IO)
Time Limits: 1000 ms Memory Limits: 131072 KB Detailed Limits

Description
世界上一共有N個JYY願意去的城市,分別從1編號到N。JYY選出了K個他一定要乘坐的航班。除此之外,還有M個JYY沒有特別的偏好,可以乘坐也可以不乘坐的航班。
一個航班我們用一個三元組(x,y,z)來表示,意義是這趟航班連線城市x和y,並且機票費用是z。每個航班都是往返的,所以JYY花費z的錢,既可以選擇從x飛往y,也可以選擇從y飛往x。
南京的編號是1,現在JYY打算從南京出發,乘坐所有K個航班,並且最後回到南京,請你幫他求出最小的花費。

Input
輸入資料的第一行包含兩個整數N和K;
接下來K行,每行三個整數x,y,z描述必須乘坐的航班的資訊,資料保證在這K個航班中,不會有兩個不同的航班在同一對城市之間執飛;
第K+2行包含一個整數M;
接下來M行,每行三個整數x,y,z 描述可以乘坐也可以不乘坐的航班資訊。

Output
輸出一行一個整數,表示最少的花費。資料保證一定存在滿足JYY要求的旅行方案。

Sample Input
6 3
1 2 1000
2 3 1000
4 5 500
2
1 4 300
3 5 300

Sample Output
3100

Data Constraint
對於10%的資料滿足N≤4;
對於30%的資料滿足N≤ 7;
對於額外30%的資料滿足,JYY可以只通過必須乘坐的K個航班從南京出發到達任意一個城市;
對於100%的資料滿足2≤N≤13,0≤K≤78,2 ≤M ≤ 200,1 ≤x,y ≤N,1 ≤z ≤ 10^4。

Hint
樣例說明:一個可行的最佳方案為123541。 機票所需的費用為1000+1000+300+500+300=3100。

題解

我們發現N很小。可以考慮狀態壓縮。壓縮點。怎麼壓縮點呢?我們可以發現,如果從1(南京)走回1(南京),這個可以構成一個每一個點都是偶數度的歐拉回路。歐拉回路的每一個點的度數都是偶數!這是不是可以狀態壓縮呢?

正解

壓縮每一個點的狀態(0:沒有經過、1:度數為奇數、2:度數為偶數)。然後最後將奇數度數的點兩兩匹配,跑一個floyed預處理。最後取一個最小值。

程式碼

#include<cstdio>
#include<cstring>
#define N 14 //江蘇有13個城市 #define K 79 #define M 201 #define MAX_f 1594323 using namespace std; int n,m,k,total1; int th3[N],next[(M+K)*2],head[(M+K)*2],edge[(M+K)*2],cost[(M+K)*2],must[N][N],zhuan[MAX_f][N],f[MAX_f],g[N][N]; bool bo[N]; int add(int x,int x1,int y1) { if (zhuan[x][n-x1+1]<=1) x+=th3[x1]; else x-=th3[x1]; if (zhuan[x][n-y1+1]<=1) x+=th3[y1]; else x-=th3[y1]; return x; } void insert(int x,int y,int z) { total1++; next[total1]=head[x]; head[x]=total1; edge[total1]=y; cost[total1]=z; } void zhuan_3(int k) { zhuan[k][0]=n; int p=k; while (p>0) { zhuan[k][zhuan[k][0]]=p%3; zhuan[k][0]--; p=p/3; } zhuan[k][0]=0; for (int i=1;i<=n;i++) { if (zhuan[k][i]!=0) zhuan[k][0]=zhuan[k][0]+1; } } int main() { scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) { g[i][j]=9999999; } } memset(bo,false,sizeof(bo)); int ad=0; for (int i=1;i<=k;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); ad+=z; bo[x]=true; bo[y]=true; must[x][y]=z; must[y][x]=z; if (g[x][y]>z) { g[x][y]=z; g[y][x]=z; } } scanf("%d",&m); for (int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); if (g[x][y]>z) { g[x][y]=z; g[y][x]=z; } } for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) { if (g[i][j]!=9999999) { insert(i,j,g[i][j]); } } } m=k; th3[1]=1; for (int i=2;i<=n+1;i++) { th3[i]=th3[i-1]*3; } for (int i=0;i<=th3[n+1]-1;i++) { zhuan_3(i); } for (int i=0;i<=th3[n+1]-1;i++) { f[i]=9999999; } f[2]=0; for (int i=1;i<=n;i++) { for (int j=0;j<=th3[n+1]-1;j++) { if (zhuan[j][0]==i&&f[j]<9000000) { if (zhuan[j][1]==0&&zhuan[j][2]==0&&zhuan[j][3]==1&&zhuan[j][4]==0&&zhuan[j][5]==1) { i++; i--; } if (j==772) { j++; j--; } if (zhuan[j][6]==1&&zhuan[j][5]==2&&zhuan[j][4]==1) { n++; n--; } bool bzp=true; for (int k=1;k<=n;k++) { if (zhuan[j][n-k+1]!=0) { for (int p=1;p<=n;p++) { if (must[k][p]!=0&&zhuan[j][n-p+1]==0) { int s=j; bzp=false; s=add(s,k,p); for (int q=head[p];q;q=next[q]) { int y=edge[q]; if (must[p][y]!=0&&zhuan[j][n-y+1]!=0&&y!=k) s=add(s,p,y); } if (f[s]>f[j]) f[s]=f[j]; } } } } if (bzp==false) continue; for (int k=1;k<=n;k++) { if (zhuan[j][n-k+1]!=0) { int pq=zhuan[j][n-k+1]; for (int p=head[k];p;p=next[p]) { int y=edge[p]; int s1=j; if (zhuan[j][n-y+1]==0) { s1=add(s1,k,y); if (f[s1]>f[j]+cost[p]) f[s1]=f[j]+cost[p]; } } } } } } } for (int k=1;k<=n;k++) { for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) { if (g[i][j]>g[i][k]+g[k][j]) { g[i][j]=g[i][k]+g[k][j]; } } } } for (int i=0;i<=th3[n+1]-1;i++) { if (i==772) { i++; i--; } if (f[i]>9000000) continue; bool bz=true; for (int j=1;j<=n;j++) { if (bo[j]==true&&zhuan[i][n-j+1]==0) { bz=false; break; } } if (bz==false) continue; int first=0; for (int j=1;j<=n;j++) { if (zhuan[i][n-j+1]==1) { first=j; break; } } for (int j=first+1;j<=n;j++) { if (zhuan[i][n-j+1]==1) { int s=i; s+=th3[first]; s+=th3[j]; if (f[s]>f[i]+g[first][j]) { f[s]=f[i]+g[first][j]; } if (s==782) { j++; j--; } if (zhuan[s][1]==2&&zhuan[s][4]==2&&zhuan[s][5]==2&&zhuan[s][6]==2&&zhuan[s][7]==2) { j++; j--; } } } } int min=99999999; for (int i=0;i<=th3[n+1]-1;i++) { if (f[i]>9000000) continue; bool bz=true; for (int j=1;j<=n;j++) { if (bo[j]==true&&zhuan[i][n-j+1]==0) { bz=false; break; } if (zhuan[i][n-j+1]==1) { bz=false; break; } } if (bz==false) continue; if (min>f[i]) min=f[i]; if (f[i]<100000) { f[i]++; f[i]--; } } printf("%d",min+ad); }