[BZOJ 1138] POI2009 Baj 最短回文路

做题的过程还是挺曲折的。

最下的想法是每次询问的时候把每两个点打包成新的起点,跑最短路,知道走到相同的点上或者走到一条边的两边。

然后就毫无压力的T了。。。。哭

后来在同学的提醒下,反过来思考。将相同的点 和 同一条边上的两个点 做起点,预处理每一个状态的答案,然后直接输出即可。

还是T了一点。。。。各种卡常数。。。。还是没过。。。。大哭

看题解,标算只是将原来的状态在拓展一下,这样可以降低转移的复杂度。

因为原来是这样转移的的d[i][j]=d[k][l]+2|存在边(i,k)(l,j)且两条边上的字母相同。之后状态记作d[i][j][0-25],表示i到j的路上出了开头多了一个字母,其余为回文串的最短路径。转移d[i][j][x]=d[k][j][26]+1|存在边(i,k,x),0<=x<26.   d[i][j][26]=d[i][k][x]+1|存在边(k,j,x)

这样虽然状态数增多了,但是有用状态很少,而且转移复杂度大大减小,ac无压力了生气

这道题目告诉了我可以从平衡状态数和转移复杂度上优化算法,从而减少计算量。

贴上代码

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;
#define num(x,y,k) (((x)-1)*n*27+((y)-1)*27+(k)+1)
char ch;
int flag[405][405],p[4320005],q[4320005],d[4320005],R[4320005];
int n,m,x,y,z,i,j,k,xx,yy,ans,N,l,r;
vector <int> a[405][26],b[405][26];

void print(int x){
  /*
  int i=(x-1)/(n*27)+1;
  int j=(x-1)%(n*27)/27+1;
  int k=(x-1)%(n*27)%27;
  if (d[x]==0) return;
  if (d[x]==1){
  	printf("%c",flag[i][j]);
  	return;
  }
  int kk=(p[x]-1)%(n*27)%27;
  printf("%c",kk+'a');
  print(p[p[x]]);
  printf("%c",kk+'a');*/
  for (R[l=1]=x;d[ R[l] ]>1;l++)
  	R[l+1]=p[ p[R[l]] ];

  for (int i=l;i>0;i--){
  	if (d[ R[i] ]<2) continue;
  	int k=(p[R[i]]-1)%(n*27)%27;
  	printf("%c",k+'a');
  }
  
  if (d[R[l]]==1){
  	int i=(R[l]-1)/(n*27)+1;
    int j=(R[l]-1)%(n*27)/27+1;
    printf("%c",flag[i][j]);
  }
  
  for (int i=1;i<=l;i++){
  	if (d[ R[i] ]<2) continue;
  	int k=(p[R[i]]-1)%(n*27)%27;
  	printf("%c",k+'a');
  }
}

int main(){
  scanf("%d%d",&n,&m);
  for (i=1;i<=m;i++){
  	scanf("%d %d %c\n",&x,&y,&ch);
  	a[x][ch-'a'].push_back(y);
  	b[y][ch-'a'].push_back(x);
  	flag[x][y]=ch;
  }
  memset(d,127/2,sizeof(d));
  for (i=1,r=0;i<=n;i++)
    d[ q[++r]=num(i,i,26) ]=0;
  for (i=1;i<=n;i++)
  	for (j=1;j<=n;j++)
  	if (i!=j&&flag[i][j]) d[ q[++r]=num(i,j,26) ]=1;
  
  int len;
  for (l=1;l<=r;l++){
  	x=(q[l]-1)/(n*27)+1;
  	y=(q[l]-1)%(n*27)/27+1;
  	z=(q[l]-1)%(n*27)%27;
  	if (z==26){
  	  for (i=0;i<26;i++){
  	    len=b[x][i].size();
  	    for (j=0;j<len;j++)
  	    if (d[ num(b[x][i][j],y,i) ]>d[ q[l] ]+1){
  	      d[ q[++r]=num(b[x][i][j],y,i) ]=d[ q[l] ]+1;
  	      p[ q[r] ]=q[l];
  	    }
      }
  	} else
  	{
  	  len=a[y][z].size();
  	  for (j=0;j<len;j++)
  	  if (d[ num(x,a[y][z][j],26) ]>d[ q[l] ]+1){ 
  	    d[ q[++r]=num(x,a[y][z][j],26) ]=d[ q[l] ]+1;
  	    p[ q[r] ]=q[l];
  	  } 
  	}
  }
  
  for (i=1;i<=n*n*27;i++)
    if (d[i]==d[0]) d[i]=-1;

  scanf("%d",&m);
  scanf("%d",&x); m--;
  while (m--){
  	scanf("%d",&y);
  	printf("%d",d[num(x,y,26)]);
  	if (d[num(x,y,26)]!=-1)
  	{
  	  printf(" ");
  	  print(num(x,y,26));
  	}
  	printf("\n");
  	x=y;
  }
  
  return 0;
}


你可能感兴趣的:(poi,最短路,2009)