Cry_For_theMoon  

传送门

目前全网只找到了一篇简略的 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\) 了。

update:二分被卡常了

#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;

posted on 2021-10-06 19:20  Cry_For_theMoon  阅读(79)  评论(0编辑  收藏  举报