【XVI Japan】Marching Course(图论,最优解)


【XVI Japan】Marching Course(图论,最优解)_第1张图片

http://opentrains.snarknews.info/~ejudge/team.cgi?SID=e4aa8c3db5989ae5&prob_id=6&action_139=Select+problem

委屈我就说这么完美的代码怎么可能有bug。。原来是这个oj把SG返回成WA了,害我瞎弄了好久啊。。。不多说,好好总结才最重要。

题意有点绕。有n个点m条路k分钟,再给出每条路的起点终点以及长度d和权值v。每条路还有一个印象度,计算方法是h*v/d,h是花在这条路上的时间,规定如果要从路的一边走到另一边必须花费>=d分钟,但是你可以在这条路上任意来回走(逗留)。一个人从起点1开始走,要在k分钟内回到起点。求他能获得的最大总印象度。

乍一看是很难啊,但是可以转化问题啊。这题贪心的关键是v/d。多想想就会发现环的情况不可能存在啊(因为他要走到比例最大的一条路那里再换一种路径回去的话干脆可以取来回里面的最大值然后原路返回啊)。于是这题就变成了一来一回。只考虑单程的话除了最后一段可以花任意时间,前面的都花di时间,这样肯定最优。于是再转化一下,先求到最后一段的起点之前的最优解,再在所有与它直接相连的路上找一条比例最大的,肯定最优。于是,枚举这个点啊,但是因为是来回所以时间要平分啊,所以换个角度把di*2了。

不能以单纯的最短路考虑。因为可能以最快的方式走到一个点经过的印象度不是最大的,vice versa。所以开了一个dp[i][j]表示到达i点时花费了j分钟,此时的最大印象度。

#include<bits/stdc++.h>
using namespace std;
struct node{
	int a,b;
	friend bool operator <(node a,node b){
		return a.b>b.b;
	}
};
node x[205][205];
double dp[205][1005];
double w[205];
priority_queue<node> q;
int main(){
	int n,m,k,a,b,c,d;
	double maxx=0;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=k;j++)
			dp[i][j]=-1;   
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)	
			x[i][j].a=1;  //为了避免出现0/0的情况 
	for(int i=0;i<m;++i){
		cin>>a>>b>>c>>d;
		c*=2;  //时间*2,方便dp里第二维时间整数存储 
		x[a][b]={c,d};
		x[b][a]={c,d};
	} 
	for(int i=1;i<=n;++i){
		maxx=0;
		for(int j=1;j<=n;++j){
			maxx=max(maxx,x[i][j].b*1.0/x[i][j].a);
		}
		w[i]=maxx; //从i点出发走一步的最大比例
	}
	q.push({1,0});
	dp[1][0]=0;
	maxx=w[1]*k;
	while(!q.empty()){
		node fr=q.top();
		q.pop();
		maxx=max(maxx,dp[fr.a][fr.b]+(k-fr.b)*w[fr.a]);
		for(int i=1;i<=n;++i){
			if(x[fr.a][i].b==0)
				continue;
			if(fr.b+x[fr.a][i].a<=k){
				if(dp[fr.a][fr.b]+x[fr.a][i].b>dp[i][fr.b+x[fr.a][i].a]){ //剪枝啊
					if(dp[i][fr.b+x[fr.a][i].a]==-1) //剪枝啊
						q.push({i,fr.b+x[fr.a][i].a});
					dp[i][fr.b+x[fr.a][i].a]=dp[fr.a][fr.b]+x[fr.a][i].b;
				}
			}
		}
	}
	printf("%.6f\n",maxx*2);
	return 0;
}


你可能感兴趣的:(【XVI Japan】Marching Course(图论,最优解))