目前全网只找到了一篇简略的 dp 题解...
简明题意:
数轴上有 \(n\) 个点 \(p\),每个点上有一个数字 \(a\),每个点的坐标已知。有 \(n\) 个数字 \(b\),你需要把 \(n\) 个点和数字一一匹配。设第 \(i\) 个点和数字 \(j\) 匹配,则定义其点权为 \(w_i=|a_i-b_j|+1\)。
一开始你在点 \(k\),在点权确定后,你需要找到一种方法经过每一个点至少一次。设第 \(i\) 个点 \(t_i\) 秒被第一次经过,则答案为 \(\max_{w_i+t_i}\)。假设你的速度为 \(1\)。
你可以任意决定一开始的匹配,和确定匹配之后的行走方案。最小化答案。
Data range:\(n\le 16\)。
分析:
神仙题。
首先考虑已经经过的点形成一个区间,加上 \(n\) 很小,又出现了“匹配”这种字眼,容易想到区间状压 dp:设 \(f(l,r,mask,0/1)\) 是走完了 \([l,r]\),匹配完了 \(mask\) 里的数字,最后站在左端点/右端点上,所花的最小时间。
很快你发现这个转移是有问题的:因为这里存在两个“时间”:一个是你当前的 \(\sum t\),即用时,一个是目前已经出现的 \(\max\{w_i+t_i\}\),你的状态不能只记录其中的一个,又不可能记录两个。
为了控制变量,可以采用二分 \(\max\{w_i+t_i\}\) 的方式,正确性来讲应该是无问题的(我有空实现一发),但大概率会 TLE。
官方题解用了一个很神仙的方法:我们把这个东西倒过来做,状态里记录当前的 \(\max\{w_i+t_i\}\) 的值。每次向前推的时候,之前所有的 \(t_i\) 都会加上你要走的距离。所以直接加上去就好了。状态 \(f(l,r,mask,0/1)\) 表示 \([l,r]\) 还未处理,\(mask\) 集合里的数字未备选,当前站在左端点/右端点上,后面已经选完的部分的 \(\max\{w_i+t_i\}\) 的值。
时间复杂度 \(O(n2^n)\)。
这里有一个细节是我们不需要 \(r\) 这个变量,确定了 \(l\) 和 \(|mask|\) 就能知道 \(r\) 了。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define next Cry_For_theMoon
#define il inline
#define pb(x) push_back(x)
#define is(x) insert(x)
#define sit set<int>::iterator
#define mapit map<int,int>::iterator
#define pi pair<int,int>
#define ppi pair<int,pi>
#define pp pair<pi,pi>
#define fr first
#define se second
#define vit vector<int>::iterator
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;
using namespace std;
const int MAXN=16,MAXM=(1<<16),INF=1e9;
int n,k,dis[MAXN],st[MAXN],ed[MAXN],ans=INF;
int g[MAXM],f[MAXN][MAXM][2];
int dp(int l,int mask,int flag){
if(g[mask]==n)return 0;
int& ret=f[l][mask][flag];if(ret!=INF)return ret;
int r=l+g[mask]-1;int pos;if(flag==0)pos=l;else pos=r;
if(l!=0){
rep(i,0,n-1){
if(mask&(1<<i))continue;
ret=min(ret,max(abs(st[l-1]-ed[i])+1,dp(l-1,mask|(1<<i),0))+dis[pos]-dis[l-1]);
}
}
if(r!=n-1){
rep(i,0,n-1){
if(mask&(1<<i))continue;
ret=min(ret,max(abs(st[r+1]-ed[i])+1,dp(l,mask|(1<<i),1))+dis[r+1]-dis[pos]);
}
}
return ret;
}
int solve(){
rep(i,1,n-1)dis[i]+=dis[i-1];
rep(i,1,(1<<n)-1){g[i]=1+g[i-lowbit(i)];}
rep(i,0,n-1)rep(j,0,(1<<n)-1)rep(k,0,1)f[i][j][k]=INF;
rep(i,0,n-1){
ans=min(ans,max(abs(st[k]-ed[i])+1,dp(k,(1<<i),0)));
}
return ans;
}
class TicketPrinters{
public:
int minTime(int currentPrinter,vector<int>printerDistance,vector<int>startingValues,vector<int>wantedValues){
k=currentPrinter;n=printerDistance.size()+1;
rep(i,1,n-1)dis[i]=printerDistance[i-1];
rep(i,0,n-1){
st[i]=startingValues[i];
ed[i]=wantedValues[i];
}
return solve();
}
}test;