状态压缩!
人生中首次写状压DP,不像ouuan大佬那样一遍AC,而是反复调了好久…而且代码还极丑陋…
题目大意:给您一张无向图,带点权。要求值最大的曼哈顿路径的值,并计数。
一条曼哈顿路径的值:所有点权之和,加相邻两点的乘积,加相邻三点的乘积(如果第一点和第三点连通)。
数据范围13,显然是状压DP。
状态表示:(1<<j)为1表示此点被访问过,为0则未访问过。
那么f[i][j][k]表示状态为i,最后一个点为j,倒数第二个点为k的最大值。num[i][j][k]则表示方案数。
DP的时候直接暴力循环就行了。
需要注意的两点:
- 路径数要
>>1,因为正反走只算一条线。 - 需要特判各种状态行不行。这个是重点,巨烦人……
下面放上我丑陋的代码和标程,附带对拍器(对拍真好用)。
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 typedef long long LL; 5 inline int read() 6 { 7 int ans=0;char ch=getchar(); 8 while(ch<'0'||ch>'9') 9 ch=getchar(); 10 while(ch>='0'&&ch<='9') 11 ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar(); 12 return ans; 13 } 14 int chart[14][14],V[14],n; 15 LL f[1<<13][14][14],num[1<<13][14][14]; 16 bool check(int i,int j,int k) 17 { 18 return chart[j][k]&&((1<<j)&i)&&((1<<k)&i); 19 } 20 int cal(int x) 21 { 22 int a[14],ans=0; 23 for(int i=0;i<14;i++) a[i]=bool(x&(1<<i)); 24 for(int i=13;i>=0;i--) ans=ans*10+a[i]; 25 return ans; 26 } 27 /** 28 2 29 3 3 30 2 2 2 31 1 2 32 2 3 33 3 1 34 */ 35 void solve(int i,int j,int k) 36 { 37 //printf("solve:%d %d %d %d ",cal(i),j,k,f[i][j][k]); 38 int t=0,ii=i; 39 while(ii) 40 t+=(ii&1),ii>>=1; 41 if(t==1) return; 42 if(t==2) 43 { 44 num[i][j][k]=1; 45 f[i][j][k]=V[j]+V[k]+V[j]*V[k]; 46 //printf("%d\n",f[i][j][k]); 47 return; 48 } 49 for(int x=0;x<n;x++) 50 { 51 if(((1<<x)&i)&&chart[x][k]&&(x!=j)&&f[i^(1<<j)][k][x]) 52 { 53 int temp = f[i^(1<<j)][k][x] + V[j] + V[j]*V[k] + (chart[x][j])*V[x]*V[k]*V[j]; 54 //printf("x=%d temp:%d=%d+%d+%d+%d ",x,temp,f[i^(1<<j)][k][x],V[j],V[j]*V[k],(chart[x][j])*V[x]*V[k]*V[j]); 55 if(temp>f[i][j][k]) 56 { 57 f[i][j][k]=temp; 58 num[i][j][k]=num[i^(1<<j)][k][x]; 59 } 60 else if(temp==f[i][j][k]) 61 num[i][j][k]+=num[i^(1<<j)][k][x]; 62 } 63 } 64 //printf("%d\n",f[i][j][k]); 65 return; 66 } 67 int main() 68 { 69 //freopen("my.in","r",stdin); 70 //freopen("my.out","w",stdout); 71 int T = read(); 72 while(T--) 73 { 74 n=read(); 75 int m=read(),x,y; 76 int N = 1<<n; 77 for(int i=1;i<=n;i++) V[i-1]=read(); 78 for(int i=1;i<=m;i++) 79 { 80 x=read();y=read(); 81 x--;y--; 82 chart[x][y]=chart[y][x]=1; 83 } 84 if(n==1) 85 { 86 printf("%d 1\n",V[0]); 87 continue; 88 } 89 ///初始化 90 for(int i=0;i<n;i++) 91 { 92 f[1<<i][i][0]=V[i]; 93 num[1<<i][i][0]=1; 94 } 95 ///DP 96 for(int i=1;i<N;i++) 97 { 98 for(int j=0;j<n;j++) 99 { 100 for(int k=0;k<n;k++) 101 { 102 if(check(i,j,k)) solve(i,j,k); 103 } 104 } 105 } 106 ///统计答案 107 LL ans=0,ans2=0; 108 for(int j=0;j<n;j++) 109 { 110 for(int k=0;k<n;k++) 111 { 112 if(ans<f[N-1][j][k]) 113 { 114 ans=f[N-1][j][k]; 115 ans2=num[N-1][j][k]; 116 } 117 else if(ans==f[N-1][j][k]) 118 { 119 ans2+=num[N-1][j][k]; 120 } 121 } 122 } 123 printf("%I64d %I64d\n",ans,ans2>>1); 124 /// 125 memset(f,0,sizeof(f)); 126 memset(num,0,sizeof(num)); 127 memset(chart,0,sizeof(chart)); 128 } 129 return 0; 130 }