萌新第二道TC(第一道是水题)!先水个题解再说。
TC题目页面传送门
给定(n imes m)字符矩阵(a)表示一个花园,每个字符有以下可能:
- 为( exttt R),表示你初始站在这个位置。保证这样的位置唯一;
- 为( exttt W),表示水井。保证这样的位置唯一;
- 为一个数字(x),表示当前位置有花,且需水量为(x)。保证(xin[1,5]),最多有(4)个这样的位置;
- 为( exttt.),表示空地。
给定一个最大载水量(lim)。你初始有(0)滴水,你可以走到水井旁边(四连通处)打水,或走到花旁边浇水。每向一个四连通的格子走需要(1mathrm s),每打/浇一滴水需要(1mathrm s),水井处和花处不能走。求浇完所有花所需要的时间,或报告无解。
(n,min[1,50],limin[1,5])。
TC的题就是这样,数据范围老是小的一批,但还真就不简单。
不难想到DP。设(dp_{i,j,k_0,k_1,k_2,k_3})表示目前在位置(i),载了(j)滴水,第(x)朵花还需要(k_x)滴水所需要的最小时间。转移也很容易想,在(i)上,可以向四连通的四个格子转移;在(j)上,若此时在水井旁边,可以向(in(j,lim])的载水量转移;在(k)上,若此时在某一朵花(x)旁边,可以向([max(0,k_x-j),k_x))的需水量转移。这样转移量是常数级的,配上(5^5mathrm O(nm))的状态数,理论上似乎是勉强可以过的。
可是,当你准备要写的时候,发现不知道DP顺序。事实上,这样的状态是有后效性的,当(j,k_x)都相等时,(i)这个维度上可能会出现环形转移。这就需要一个经典的解决方法:最短路。由于这里的状态转移方程是典型的(dp_i=minlimits_{jin trs_i}{dp_j+cst(i,j)})形式,其中(trs_i)表示(i)的决策集合,(cst(i,j))表示(i)转移到(j)的代价,我们可以对每个状态建个节点,转移对之间连有向边,边权为转移代价,跑最短路,最终每个节点的DP值就是它到起点的最短路加上起点的DP值。
然鹅虽然解决了可做性问题,由于有堆优化Dijkstra,要在“勉强可以过”的复杂度+常数上再乘一个(log),已经吃不消了。考虑优化。
不难发现,每种除了空地的元素的数量都限制的很小,于是空地就很多,而你在空地里面乱跑兜圈是没有意义的,于是就有了很多无意义的转移。考虑把起点、所有可以浇花/打水的格子设为关键点,只有从关键点到关键点的最短路径才是有意义的。于是我们把状态改一下,第一维(i)表示第(i)个关键点。在DP之前,预处理出关键点与关键点之间的最短路,这个xjb随便算即可,由于DP要写Dijkstra,所以这里也用Dijkstra套一下。算一下,关键点最多有(5 imes4+1=21)个,虽然转移的时候要转移到所有的关键点(而不是像之前那样四连通四个格子),但也比之前的复杂度好太多了。
码量有点大,是个码农题。
代码:
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define X first
#define Y second
#define mp make_pair
const int inf=1e7;
const int N=50,M=50;
vector<pair<int,int> > pt;
int dis_pt[21][21];
bool w[N+1][M+1],f[N+1][M+1][4];
int id[21][6][6][6][6][6];
int now;
vector<pair<int,int> > nei[163296];
int dis[163296];//就是DP值了
bool vis[163296];
void dijkstra(int st){//Dijkstra来DP
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
q.push(mp(dis[st]=0,st));
while(q.size()){
int x=q.top().Y;
q.pop();
if(vis[x])continue;
vis[x]=true;
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i].X,len=nei[x][i].Y;
if(dis[x]+len<dis[y])q.push(mp(dis[y]=dis[x]+len,y));
}
}
}
struct WaterBot{
int minTime(vector<string> a,int lim){
int n=a.size(),m=a[0].size(),cnt=0,st_x,st_y,st;
vector<int> flower;
for(int i=0;i<n;i++)for(int j=0;j<m;j++)
if(isdigit(a[i][j])||a[i][j]=='W'){
if(isdigit(a[i][j]))cnt++,flower.pb(a[i][j]^48);
if(i>0)pt.pb(mp(i-1,j)),(isdigit(a[i][j])?f[i-1][j][flower.size()-1]:w[i-1][j])=true;
if(i+1<n)pt.pb(mp(i+1,j)),(isdigit(a[i][j])?f[i+1][j][flower.size()-1]:w[i+1][j])=true;
if(j>0)pt.pb(mp(i,j-1)),(isdigit(a[i][j])?f[i][j-1][flower.size()-1]:w[i][j-1])=true;
if(j+1<m)pt.pb(mp(i,j+1)),(isdigit(a[i][j])?f[i][j+1][flower.size()-1]:w[i][j+1])=true;
}
else if(a[i][j]=='R')pt.pb(mp(i,j)),st_x=i,st_y=j;
while(flower.size()<4)flower.pb(0);
sort(pt.begin(),pt.end());
pt.resize(unique(pt.begin(),pt.end())-pt.begin());//所有关键点
for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(!isdigit(a[i][j])&&a[i][j]!='W'){//建图
if(i+1<n&&!isdigit(a[i+1][j])&&a[i+1][j]!='W')nei[i*m+j].pb(mp((i+1)*m+j,1)),nei[(i+1)*m+j].pb(mp(i*m+j,1));
if(j+1<m&&!isdigit(a[i][j+1])&&a[i][j+1]!='W')nei[i*m+j].pb(mp(i*m+j+1,1)),nei[i*m+j+1].pb(mp(i*m+j,1));
}
for(int i=0;i<pt.size();i++){//算关键点与关键点的距离
if(pt[i].X==st_x&&pt[i].Y==st_y)st=i;
dijkstra(pt[i].X*m+pt[i].Y);
for(int j=0;j<pt.size();j++)dis_pt[i][j]=dis[pt[j].X*m+pt[j].Y];
// for(int j=0;j<pt.size();j++)cout<<pt[j].X<<","<<pt[j].Y<<":"<<dis_pt[i][j]<<" ";puts("");
}
for(int i=0;i<n*m;i++)nei[i].clear();
for(int i=0;i<pt.size();i++)for(int j=0;j<=5;j++)for(int k=0;k<=(cnt>=1)*5;k++)for(int o=0;o<=(cnt>=2)*5;o++)for(int p=0;p<=(cnt>=3)*5;p++)for(int q=0;q<=(cnt>=4)*5;q++)
id[i][j][k][o][p][q]=now++;
for(int i=0;i<pt.size();i++)for(int j=0;j<=5;j++)for(int k=0;k<=(cnt>=1)*5;k++)for(int o=0;o<=(cnt>=2)*5;o++)for(int p=0;p<=(cnt>=3)*5;p++)for(int q=0;q<=(cnt>=4)*5;q++){//重新建DP的图
for(int ii=0;ii<pt.size();ii++)if(i!=ii)nei[id[i][j][k][o][p][q]].pb(mp(id[ii][j][k][o][p][q],dis_pt[i][ii]));
if(w[pt[i].X][pt[i].Y])for(int ii=j+1;ii<=lim;ii++)nei[id[i][j][k][o][p][q]].pb(mp(id[i][ii][k][o][p][q],ii-j));
if(f[pt[i].X][pt[i].Y][0])for(int ii=max(0,k-j);ii<k;ii++)nei[id[i][j][k][o][p][q]].pb(mp(id[i][j-(k-ii)][ii][o][p][q],k-ii));
if(f[pt[i].X][pt[i].Y][1])for(int ii=max(0,o-j);ii<o;ii++)nei[id[i][j][k][o][p][q]].pb(mp(id[i][j-(o-ii)][k][ii][p][q],o-ii));
if(f[pt[i].X][pt[i].Y][2])for(int ii=max(0,p-j);ii<p;ii++)nei[id[i][j][k][o][p][q]].pb(mp(id[i][j-(p-ii)][k][o][ii][q],p-ii));
if(f[pt[i].X][pt[i].Y][3])for(int ii=max(0,q-j);ii<q;ii++)nei[id[i][j][k][o][p][q]].pb(mp(id[i][j-(q-ii)][k][o][p][ii],q-ii));
}
dijkstra(id[st][0][flower[0]][flower[1]][flower[2]][flower[3]]/*起点,DP值为0*/);//DP
int ans=inf;
for(int i=0;i<pt.size();i++)for(int j=0;j<=5;j++)ans=min(ans,dis[id[i][j][0][0][0][0]]);//目标
return ans>=inf?-1:ans;
}
};