记录编号 85440 评测结果 AAAAAAAAAA
题目名称 [CEPC 2003] 骰子游戏 最终得分 100
用户昵称 Gravatarcstdio 是否通过 通过
代码语言 C++ 运行时间 1.973 s
提交时间 2014-01-04 11:09:02 内存使用 0.35 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<iomanip>
using namespace std;
typedef long long ll;
const ll INF=1e17;
const int SIZEN=145,SIZEH=4,NUMSIDE=6;//行数/哪一面朝上,共24种状态
const int SIZECIR=20;//这里定为:最多迂回10格,即"尝试区"大小(0~SIZECIR-1)
int N;//有意义的状态数
int delta;//即x2-x1,相差列数
pair<int,pair<int,int> > state[SIZEN+1];//每个状态是在哪一行,哪一面朝上,哪一面朝前
int statelis[SIZEH+1][NUMSIDE+1][NUMSIDE+1]={0};//哪一行,哪一面朝上,哪一面朝前对应的状态编号
int S;//起点
vector<int> T;//终点集对应的状态们
int sticker[NUMSIDE+1]={0};//骰子每一面上的数
int opposite[NUMSIDE+1]={0};//每个面都和哪个面相对
int onleft[NUMSIDE+1][NUMSIDE+1]={0};//当i在上面j在前面时,左侧的面
vector<pair<int,int> > c[SIZEN*SIZECIR+1];//在"尝试区"内的邻接表
int TA_NUMS;//"尝试区"内的状态总数
void printstate(int x){//调试接口,输出状态x的具体含义
	//格式为:x,y,up,front
	int temp=x%N;
	cout<<"(";
	cout<<(x-1)/N<<",";
	cout<<state[temp].first<<","<<state[temp].second.first<<","<<state[temp].second.second<<")";
}
void printgraph(void){//调试接口,输出图
	for(int i=1;i<=TA_NUMS;i++){
		printstate(i);
		cout<<" lis:";
		for(int j=0;j<c[i].size();j++){
			cout<<"**";
			printstate(c[i][j].first);
			cout<<" "<<c[i][j].second<<".. ";
		}
		cout<<endl;
	}
}
void printarray(ll *s,ll n){//调试接口,从地址s开始连续输出n个元素
	for(int i=0;i<n;i++){
		if(s[i]!=INF) cout<<s[i]<<" ";
		else cout<<-1<<" ";
	}
	cout<<endl;
}
void printarray(int *s,int n){//调试接口,从地址s开始连续输出n个元素
	for(int i=0;i<n;i++){
		if(s[i]!=INF) cout<<s[i]<<" ";
		else cout<<-1<<" ";
	}
	cout<<endl;
}
class MATRIX{
public:
	int n,m;//n行m列
	ll s[SIZEN+1][SIZEN+1];
	MATRIX(){//初始化为零
		m=n=0;
		for(int i=0;i<SIZEN;i++) for(int j=0;j<SIZEN;j++) s[i][j]=INF;
		for(int i=0;i<SIZEN;i++) s[i][i]=0;
		//初始化为无边的矩阵(权值均为inf)
	}
	void print(void){//调试接口,输出矩阵(若为inf则相应项输出0)
		cout<<"size:"<<n<<" "<<m<<endl;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(s[i][j]<=INF) cout<<s[i][j]<<" ";
				else cout<<-1<<" ";
			}
			cout<<endl;
		}
	}
	void printele(void){//调试接口,输出整个矩阵,其中对s[i][j]输出:i状态的意义,j状态的意义,(i,j)的值
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				printstate(i);cout<<"->";printstate(j);
				cout<<s[i][j]<<endl;
			}
		}
	}
	void assign_one(int sn){//赋值为sn行的无边矩阵
		n=m=sn;
		for(int i=1;i<=n;i++) s[i][i]=0;
	}
};
MATRIX operator * (MATRIX a,MATRIX b){//矩阵乘法,结果对MOD取模
	MATRIX c;
	c.n=a.n;c.m=b.m;
	int i,j,k;
	for(i=1;i<=c.n;i++){
		for(j=1;j<=c.m;j++){
			c.s[i][j]=INF;
			for(k=1;k<=a.m;k++){
				if(a.s[i][k]==INF||b.s[k][j]==INF) continue;
				c.s[i][j]=min(c.s[i][j],a.s[i][k]+b.s[k][j]);
			}
		}
	}
	return c;
}
MATRIX quickpow(MATRIX a,ll n){//a^n,a的长宽相等
	MATRIX ans;ans.assign_one(a.n);
	while(n){
		if(n&1)	ans=ans*a;
		a=a*a;
		n>>=1;
	}
	return ans;
}
void SPFA(vector<pair<int,int> > c[],int s,int n,ll f[]){//SPFA
	//图的信息在邻接表c中,以s为起点,节点1~n,最短路信息储存在f中
	queue<int> Q;
	bool *inq=new bool[n+1];
	int i,u,v,w;
	for(i=1;i<=n;i++) f[i]=INF,inq[i]=false;
	inq[s]=true,f[s]=0,Q.push(s);
	while(!Q.empty()){
		u=Q.front(),Q.pop();
		inq[u]=false;
		for(i=0;i<c[u].size();i++){
			v=c[u][i].first,w=c[u][i].second;
			if(f[v]>f[u]+w){
				f[v]=f[u]+w;
				if(!inq[v]) inq[v]=true,Q.push(v);
			}
		}
	}
}
void makegraph(MATRIX &I,MATRIX &G){//得到起点到第一行的最短路矩阵I和这一行到下一行的最短路矩阵G
	ll f[SIZEN*SIZECIR+1]={0};
	int i,j;
	I.n=1,I.m=N;
	int TN=SIZECIR/2-1;
	SPFA(c,S+TN*N,TA_NUMS,f);
	for(i=1;i<=N;i++) I.s[1][i]=f[i+TN*N];
	G.n=G.m=N;
	for(i=1;i<=N;i++){
		SPFA(c,i,TA_NUMS,f);
		for(j=1;j<=N;j++) G.s[i][j]=f[j+N];
	}
}
inline int left(int up,int fr){//这个状态的骰子(上面是up,前面是fr),左边是哪个面
	return onleft[up][fr];
}
inline int right(int up,int fr){
	return opposite[onleft[up][fr]];//这个状态的骰子,右面是哪个面
}
void maketa(void){//得到"尝试区"的邻接表,"ta"就是尝试区
	int x,y,up,fr,u,v;
	TA_NUMS=SIZECIR*N;//一列的状态数是N,共SIZECIR列
	for(x=0;x<SIZECIR;x++){
		for(y=1;y<=SIZEH;y++){
			for(up=1;up<=NUMSIDE;up++){
				for(fr=1;fr<=NUMSIDE;fr++){
					if(up==fr||opposite[up]==fr) continue;
					u=statelis[y][up][fr]+x*N;//对不同行的状态进行"连续编号"
					if(x>0){//向左滚,右变上,前不变
						v=statelis[y][right(up,fr)][fr]+(x-1)*N;
						c[u].push_back(make_pair(v,sticker[right(up,fr)]));
					}
					if(x<SIZECIR-1){//向右滚,左变上,前不变
						v=statelis[y][left(up,fr)][fr]+(x+1)*N;
						c[u].push_back(make_pair(v,sticker[left(up,fr)]));
					}
					if(y<SIZEH){//向上滚,前变上,下变前
						v=statelis[y+1][fr][opposite[up]]+x*N;
						c[u].push_back(make_pair(v,sticker[fr]));
					}
					if(y>1){//向下滚,后变上,上变前
						v=statelis[y-1][opposite[fr]][up]+x*N;
						c[u].push_back(make_pair(v,sticker[opposite[fr]]));
					}
				}
			}
		}
	}
}
void init(void){
	int i,j,k,x1,x2,y1,y2,tot=0;
	for(i=1;i<=NUMSIDE;i++) scanf("%d",&sticker[i]);
	opposite[1]=6,opposite[6]=1;
	opposite[2]=5,opposite[5]=2;
	opposite[3]=4,opposite[4]=3;
	for(i=1;i<=SIZEH;i++){//这里得出了所有的状态
		for(j=1;j<=NUMSIDE;j++){
			for(k=1;k<=NUMSIDE;k++){
				if(j==k||opposite[j]==k) continue;
				tot++;
				state[tot]=make_pair(i,make_pair(j,k));
				statelis[i][j][k]=tot;
			}
		}
	}
	N=tot;//合法的状态总数
	scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
	if(x1>x2){//将棋盘水平旋转180度
		//上下不变
		swap(sticker[2],sticker[5]);//前后交换,即2和5对调
		swap(sticker[3],sticker[4]);//左右交换,即3和4对调
		swap(x1,x2);//横坐标交换
		y1=SIZEH+1-y1,y2=SIZEH+1-y2;//纵坐标翻折
	}
	if(x1!=0) x2-=x1,x1=0;//平移至最左边
	delta=x2-x1;
	S=statelis[y1][1][2];//最开始1号面朝上,2号面朝前
	tot=0;
	for(i=1;i<=NUMSIDE;i++){
		for(j=1;j<=NUMSIDE;j++){
			if(i==j||opposite[i]==j) continue;
			T.push_back(statelis[y2][i][j]);//纵坐标为y2,每一种状态都有可能
		}
	}
	onleft[1][2]=3,onleft[1][3]=5;
	onleft[2][1]=4,onleft[2][3]=1;
	onleft[3][1]=2,onleft[3][2]=6;
	for(i=1;i<=NUMSIDE;i++){
		for(j=1;j<=NUMSIDE;j++){
			if(i==j||opposite[i]==j) continue;
			if(onleft[i][j]) continue;
			else if(onleft[i][opposite[j]]) onleft[i][j]=opposite[onleft[i][opposite[j]]];
			else if(onleft[opposite[i]][j]) onleft[i][j]=opposite[onleft[opposite[i]][j]];
			else if(onleft[opposite[i]][opposite[j]]) onleft[i][j]=onleft[opposite[i]][opposite[j]];
		}
	}
	//至此生成了onleft数组,由此可以轻松得到某个状态下骰子所有位置的面都是什么
}
void work(void){
	maketa();
	MATRIX G,I;
	makegraph(I,G);
	I=I*quickpow(G,delta);
	ll ans=INF;
	for(int i=0;i<T.size();i++) ans=min(ans,I.s[1][T[i]]);
	cout<<ans<<endl;
}
int main(){
	freopen("dicecontest.in","r",stdin);
	freopen("dicecontest.out","w",stdout);
	init();
	work();
	return 0;
}