[洛谷P3486]POI2009 KON-Ticket Inspector

问题描述

Byteasar works as a ticket inspector in a Byteotian National Railways (BNR) express train that connects Byteburg with Bitwise.

The third stage of the BNR reform (The never ending saga of BNR reforms and the Bitwise hub was presented in the problems Railway from the third stage of XIV Polish OI and Station from the third stage of XV Polish OI. Their knowledge, however, is not required at all in order to solve this problem.) has begun. In particular, the salaries system has already been changed.

For example, to encourage Byteasar and other ticket inspectors to efficient work, their salaries now depend on the number of tickets (passengers) they inspect. Byteasar is able to control all the passengers on the train in the time between two successive stations, but he is not eager to waste his energy in doing so. Eventually he decided he would check the tickets exactly times per ride.

Before setting out, Byteasar is given a detailed summary from which he knows exactly how many passengers will travel from each station to another. Based on that he would like to choose the moments of control in such a way that the number of passengers checked is maximal. Obviously, Byteasar is not paid extra for checking someone multiple times - that would be pointless, and would only disturb the passengers. Write a programme that will determine for Byteasar when he should check the tickets in order to maximise his revenue.

有n个车站,现在有一辆火车从1到n驶过,给出(a_ij)代表从i站上车j站下车的人的个数。列车行驶过程中你有K次检票机会,所有当前在车上的人会被检票,问最多能检多少个不同的人的票。

传送门

题面

题解

由定义就可以得出状态的表达方式:设(f[i][j])表示在第j个车站时检第i次票的最大检票人数,那么显然(f[i][j])由前面某一个(f[i-1][k])转移过来。那么需要确定的是从k到j有多少人在此时间段上车且没有下车。输入中的矩阵是锯齿形的,实际上全部向右边掉落后可以转化为一个矩阵a,其中(a[i][j])表示在i上车j下车的人数。那么如何找到在一个区间内上车的人数呢?可以发现,在i时还在的人数就是所有在i之前到、在i之后走的人。那么对应的就是矩阵a中满足横坐标不大于i、纵坐标大于i的点的权值和。可以用类似前缀和的方法来计算。扩展到区间,一个时间段(i,j)中在i之后下车且在j之前没有下车的人就是横坐标大于i小于j、纵坐标大于j的点的和。所以,设(sum[i][j])表示以(x,y)为左下角、(1,n)为右下角的矩阵和,则状态转移方程如下:

[f[i][j]=Max(f[i-1][k]+sum[j][j+1]-sum[k][j+1])_{k=1...j} ]

答案为:

[ans=Max(f[m][i])_{i=1...n} ]

接下来的问题是如何输出路径。记(pre[i][j])表示状态(f[i][j])是从哪一个车站转移过来的,在确定答案后倒推回去即为最佳方案(记得倒序输出)。

代码

#include <iostream>
#include <cstdio>
#include <vector>
#define N 1002
using namespace std;
int n,m,i,j,k,a[N][N],f[N][N],sum[N][N],pre[N][N];
vector<int> v;
int main()
{
	cin>>n>>m;
	for(i=1;i<=n;i++){
		for(j=i+1;j<=n;j++) cin>>a[i][j];
	}
	for(i=1;i<=n;i++){
		for(j=n;j>=1;j--) sum[i][j]=sum[i-1][j]+sum[i][j+1]-sum[i-1][j+1]+a[i][j];
	}
	for(i=1;i<=m;i++){
		for(j=1;j<=n;j++){
			for(k=i-1;k<j;k++){
				if(f[i-1][k]+sum[j][j+1]-sum[k][j+1]>f[i][j]){
					f[i][j]=f[i-1][k]+sum[j][j+1]-sum[k][j+1];
					pre[i][j]=k;
				}
			}
		}
	}
	int maxx=0,ans=0;
	for(i=m;i<=n;i++){
		if(f[m][i]>maxx){
			maxx=f[m][i];
			ans=i;
		}
	}
	int p=ans;
	for(i=m;i>=1;i--){
		v.push_back(p);
		p=pre[i][p];
	}
	for(i=v.size()-1;i>=0;i--) cout<<v[i]<<' ';
	cout<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/LSlzf/p/10703322.html