P7685 [CEOI2005] Mobile Service 题解
lemondinosaur · · 题解
题目传送门
自然想到设
易证存在最优方案使得每次服务后三人所在位置互不相同。
考虑到有一个位置肯定为
重新设方程就是
若由
如果
如果
如果
第二、三种情况会交换服务员的编号。
那如何输出方案呢?
交换编号和当前的
如果每一次服务的
再看上面三种情况,第一种
第二种
那么在第二种或第三种情况记录
如果被记录,就把
为什么可以直接替换,因为
所以
然后记录下每次
记录一个编号直接用 unsigned char 即可,内存为 42MB 左右。
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
const int N=1011,M=211; unsigned char pos[N][M][M];
int m,n,f[2][M][M],res[N],a[N],cost[M][M],X[N],Y[N];
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int main(){
m=iut(); n=iut();
for (int i=1;i<=m;++i)
for (int j=1;j<=m;++j) cost[i][j]=iut();
memset(f[0],0x3f,sizeof(f[0]));
f[0][1][2]=0,a[0]=3,X[0]=1,Y[0]=2;
for (int i=1;i<=n;++i) a[i]=iut();
for (int i=1;i<=n;++i){
memset(f[i&1],0x3f,sizeof(f[i&1]));
for (int j=1;j<=m;++j)
for (int k=1;k<=m;++k) if (j!=k)
if (f[(i&1)^1][j][k]<f[0][0][0]){
if (j!=a[i]&&k!=a[i]){
int t=f[(i&1)^1][j][k]+cost[a[i-1]][a[i]];
if (f[i&1][j][k]>t) f[i&1][j][k]=t,pos[i][j][k]=0;
}
if (a[i-1]!=a[i]&&k!=a[i]){
int t=f[(i&1)^1][j][k]+cost[j][a[i]];
if (f[i&1][a[i-1]][k]>t)
f[i&1][a[i-1]][k]=t,pos[i][a[i-1]][k]=j;
}
if (a[i-1]!=a[i]&&j!=a[i]){
int t=f[(i&1)^1][j][k]+cost[k][a[i]];
if (f[i&1][j][a[i-1]]>t)
f[i&1][j][a[i-1]]=t,pos[i][j][a[i-1]]=k;
}
}
}
int ans=f[0][0][0],mij,mik,F=1,G=2;
for (int j=1;j<=m;++j)
for (int k=1;k<=m;++k)
if (j!=k&&f[n&1][j][k]<ans)
ans=f[n&1][j][k],mij=j,mik=k;
printf("%d\n",ans);
for (int i=n;i>=1;--i){
int t=pos[i][mij][mik]; X[i]=mij,Y[i]=mik;
if (t) (mik==a[i-1])?(mik=t):(mij=t);
}
for (int i=1;i<=n;++i){
if (X[i]==a[i-1]) F=6-F-G;
else if (Y[i]==a[i-1]) G=6-F-G;
printf("%d ",6-F-G);
}
return 0;
}