TopCoder 2428

萌新第二道TC(第一道是水题)!先水个题解再说。

TC题目页面传送门

给定(n imes m)字符矩阵(a)表示一个花园,每个字符有以下可能:

  1. ( exttt R),表示你初始站在这个位置。保证这样的位置唯一;
  2. ( exttt W),表示水井。保证这样的位置唯一;
  3. 为一个数字(x),表示当前位置有花,且需水量为(x)。保证(xin[1,5]),最多有(4)个这样的位置;
  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;
	}
};
原文地址:https://www.cnblogs.com/ycx-akioi/p/TopCoder-2428.html