1. 程式人生 > 其它 >【題解】【CF1065D Three Pieces】

【題解】【CF1065D Three Pieces】

Analysis

不難想到設f[p][0/1/2]表示走到標號為p的格子,目前是車/馬/象的最短時間。那麼轉移時,需要知道從一個格子的某個狀態走向另一個格子的某個狀態的最短時間。
即f[p][q]=min f[p-1][y]+d[p-1,y][p,q]
那麼將p,q可以壓成一個狀態,將同一個格子間不同狀態的轉化和同一個狀態不同格子間的跳躍連好邊,跑一遍floyed求出d陣列,再轉移f即可。
至於第二問在時間最短情況下,轉換次數最少。實際上就是雙關鍵字dp。這裡有兩個套路:

  1. 先預估第二關鍵字的最大影響範圍,設為M,則將dp值改為第一關鍵字*M+第二關鍵字。這樣,在第一關鍵字不同的情況下,最終答案一定是取決於第一關鍵字,第一關鍵字相同時,第二關鍵字更優的答案更優。
  2. 設一個與原dp陣列規模完全相同的陣列,表示當該狀態取到最優時,第二關鍵字最優為多少。該陣列既可以再跑完原dp陣列後重新跑一遍,也可以一遍跑原dp陣列一邊更新該陣列,具體為:當原dp陣列更新時,則該陣列強制更新,否則,若原dp陣列的值與此次更新的值相同時,則可以更新該陣列。
    下面程式碼採用了第二種。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
	int x=0,w=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') {w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
inline void write(int x)
{
	if(x<0) putchar('-'),x=~(x-1);
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const int N=310,dirx[8]={-2,-1,1,2,2,1,-1,-2},diry[8]={1,2,2,1,-1,-2,-2,-1};
int n,a[15][15],d[N][N],minn[N][N],f[N],g[N],cnt;
//0:車 1:馬 2:象 
signed main()
{
    n=read();for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=read();
    memset(d,0x3f,sizeof d);memset(minn,0x3f,sizeof minn);memset(f,0x3f,sizeof f);memset(g,0x3f,sizeof g);
    for(int i=1;i<=n*n;++i){
    	for(int j=0;j<3;++j)
    	{
    		int x=i*3+j,y=i*3+(j+1)%3;
    		d[x][y]=d[y][x]=1;
    		minn[x][y]=minn[y][x]=1;
		}
	}
	for(int i=1;i<=n;++i)
	for(int j=1;j<=n;++j)
	{
		int p=a[i][j]*3;
		for(int k=1;k<=j-1;++k)
		{
			if(k<=i-1) {
				int x=p+2,y=a[i-k][j-k]*3+2;
				d[x][y]=d[y][x]=1;minn[x][y]=minn[y][x]=0;
			}
			if(k<=n-i) {
				int x=p+2,y=a[i+k][j-k]*3+2;
			    d[x][y]=d[y][x]=1;minn[x][y]=minn[y][x]=0;	
			} 
		}
		for(int k=1;k<=n;++k)
		{
			if(i-k>=1) {
				int x=p,y=a[i-k][j]*3;
			    d[x][y]=d[y][x]=1;minn[x][y]=minn[y][x]=0;					
			}
			if(j-k>=1) {
				int x=p,y=a[i][j-k]*3;
			    d[x][y]=d[y][x]=1;minn[x][y]=minn[y][x]=0;					
			}
		}
		for(int k=0;k<8;++k)
		{
			int x=i+dirx[k],y=j+diry[k];
			if(x<1||x>n||y<1||y>n) continue;
			d[p+1][a[x][y]*3+1]=1;minn[p+1][a[x][y]*3+1]=0;
		}
	}
	cnt=n*n*3+2;
	for(int k=3;k<=cnt;++k)
	{
		for(int i=3;i<=cnt;++i)
		{
			for(int j=3;j<=cnt;++j)
			{
				if(d[i][j]>d[i][k]+d[k][j]){
					d[i][j]=d[i][k]+d[k][j];minn[i][j]=minn[i][k]+minn[k][j];
				}
				else if(d[i][j]==d[i][k]+d[k][j]){
					minn[i][j]=min(minn[i][j],minn[i][k]+minn[k][j]);
				}
			}
		}
	}
	for(int i=0;i<3;++i) f[3+i]=g[3+i]=0;
	for(int i=2;i<=n*n;++i)
	{
//		if(i==8){
//			int ljh=6;
//		}
		for(int j=0;j<3;++j)
		{
			int p=i*3+j;
			for(int k=0;k<3;++k)
			{
				int q=(i-1)*3+k;
				if(f[p]>f[q]+d[q][p]){
					f[p]=f[q]+d[q][p];g[p]=g[q]+minn[q][p];
				}
				else if(f[p]==f[q]+d[q][p]) g[p]=min(g[q]+minn[q][p],g[p]);
			}
//			if(j==1){
//				write(i);putchar(' ');write(f[p]);puts("");
//			}
		}
		
	}
    int ans=0x3f3f3f3f3f3f3f,res=ans;
	for(int j=0;j<3;++j) 
	ans=min(ans,f[cnt-j]);
    cout<<ans<<" ";
    for(int j=0;j<3;++j) if(ans==f[cnt-j]) res=min(res,g[cnt-j]);
    cout<<res;
	return 0;
}
/*
3
1 4 7
6 9 2
3 8 5 7+1+1
*/