ZOJ 3469 Food Delivery

ZOJ 3469 Food Delivery

题目大意:有一条街道(相当于一个数轴上的整数点),上面有n个人和一个速度为v的送餐员,给定他们的初始位置,每个人都有一个不悦值,每等1分钟就增加那个数值,要求,送餐员要怎么送餐才能使总体的不悦值最小。

PS:这题本人是用C++过的,相同的算法,C++80MS,JAVA却TLE。快速io也没救得了我,各种尝试失败之后,转成了C++~下面给了两种语言的代码,基本相差无几~而且以前设定无穷大量都是设定0x7fffffff,这次发现设定这个不够好,0x3f3f3f3f更好。

思路如下:一看到有个x轴,每个人有一个位置(感觉不会出现位置重合的情况),既然要送餐,那肯定先得排序。排序完后从送餐员的位置p开始挨个送餐,可以想象肯定是在p位置上考虑先送去左边或者先送去右边,每一次送餐员移动到下个位置后就重新考虑要送去新位置的左边还是右边,这样得到的情况会是最好的。开个三维数组dp[n+2][n+2][2],第三维0代表左,1代表右,dp[i][j][0]表示送完了区间[ i , j ]之后停在左边(i 位置上);数组sum[i]表示,前 i 个人的不悦值总和;xb[i]是类数组(结构体),记录第 i 个人的位置跟不悦值。

状态转移方程:

dp[i][j][0]=Math.min(dp[i+1][j][0]+(sum[i]+sum[n+1]-sum[j])*(xb[i+1].x-xb[i].x), dp[i+1][j][1]+(sum[i]+sum[n+1]-sum[j])*(xb[j].x-xb[i].x))

dp[i][j][1]=Math.min(dp[i][j-1][0]+(sum[i-1]+sum[n+1]-sum[j-1])*(xb[j].x-xb[i].x), dp[i][j-1][1]+(sum[i-1]+sum[n+1]-sum[j-1])*(xb[j].x-xb[j-1].x))

话说,本来这两行应该是写四句的,但是不知道为毛脑抽筋,改成两句很长很长的。。。。就拿第一个来说,计算dp[i][j][0]因为最后停留在 i 位置上(左边),所以只可能是从dp[i+1][j][0]或者dp[i+1][j][1]上转移过去的,就是说要在某个位置上跑去给 i 送餐,只要再加上跑去 i 位置时产生的总不悦值(速度v最后输出时乘上即可)。

(C++)AC代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
const int MAXN=1002;
const int INF=0x3f3f3f3f;
struct people
{
    int x,b;
};
people xb[MAXN];
int sum[MAXN];
int dp[MAXN][MAXN][2];
bool cmp(people a,people b)
{
    return a.x<b.x;
}

int main()
{
    int n,v,x;
    while(scanf("%d%d%d",&n,&v,&x)==3)
    {
        for(int i=1;i<=n;i++)
            scanf("%d%d",&xb[i].x,&xb[i].b);
        xb[n+1].x=x;
        xb[n+1].b=0;
        sort(xb+1,xb+(n+2),cmp);

        int p;
        for(int i=1;i<n+2;i++)//这个两重循环给sum数组跟dp数组初始化,并找出排序后的送餐员的位置,赋0
        {
            sum[i]=sum[i-1]+xb[i].b;
            for(int j=1;j<n+2;j++)
                dp[i][j][0]=dp[i][j][1]=INF;
            if(xb[i].x==x)
            {
                p=i;
                dp[p][p][0]=dp[p][p][1]=0;
            }
        }

        for(int i=p;i>0;i--)
            for(int j=p;j<n+2;j++)
                if(i!=j)
                {
                    dp[i][j][0]=min(dp[i+1][j][0]+(sum[i]+sum[n+1]-sum[j])*(xb[i+1].x-xb[i].x),
                                    dp[i+1][j][1]+(sum[i]+sum[n+1]-sum[j])*(xb[j].x-xb[i].x));
                    dp[i][j][1]=min(dp[i][j-1][0]+(sum[i-1]+sum[n+1]-sum[j-1])*(xb[j].x-xb[i].x),
                                    dp[i][j-1][1]+(sum[i-1]+sum[n+1]-sum[j-1])*(xb[j].x-xb[j-1].x));
                }

        printf("%d\n",v*min(dp[1][n+1][0],dp[1][n+1][1]));
    }
    return 0;
}
(JAVA)TLE代码:
import java.util.Comparator;
import java.util.Arrays;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class 动态规划2I_foodDelivery
{
	static StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
	static PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
	
	public static int nextInt()throws IOException{in.nextToken();return (int)in.nval;}
	
	public static class people{int x, b;}
	
	public static void main(String[] args)throws IOException
	{
		while(in.nextToken()!=StreamTokenizer.TT_EOF)
		{
			int n=(int)in.nval;
			int v=nextInt();
			int x=nextInt();

			people xb[]=new people[n+2];
			for(int i=1;i<n+1;i++)
			{
				xb[i]=new people();
				xb[i].x=nextInt();
				xb[i].b=nextInt();
			}
			xb[n+1]=new people();
			xb[n+1].x=x; xb[n+1].b=0;
			Arrays.sort(xb, 1, n+2, new Comparator<people>() {
				public int compare(people a, people b)
				{
					if(a.x!=b.x)
						return (a.x>b.x?1:-1);
					return 0;
				}
			});
			
			int sum[]=new int[n+2];
			int dp[][][]=new int[n+2][n+2][2];
			int p=0;
			for(int i=1;i<n+2;i++)//这个两重循环给sum数组跟dp数组初始化,并找出排序后的送餐员的位置,赋0
			{
				sum[i]=sum[i-1]+xb[i].b;
				for(int j=1;j<n+2;j++)
					dp[i][j][0]=dp[i][j][1]=0x3f3f3f3f;
				if(xb[i].x==x)
				{
					p=i;
					dp[p][p][0]=dp[p][p][1]=0;
				}
			}	

			for(int i=p;i>0;i--)
				for(int j=p;j<n+2;j++)
					if(i!=j)
					{
						dp[i][j][0]=Math.min(dp[i+1][j][0]+(sum[i]+sum[n+1]-sum[j])*(xb[i+1].x-xb[i].x), 
								dp[i+1][j][1]+(sum[i]+sum[n+1]-sum[j])*(xb[j].x-xb[i].x));
						dp[i][j][1]=Math.min(dp[i][j-1][0]+(sum[i-1]+sum[n+1]-sum[j-1])*(xb[j].x-xb[i].x), 
								dp[i][j-1][1]+(sum[i-1]+sum[n+1]-sum[j-1])*(xb[j].x-xb[j-1].x));
					}
			
			out.println(v*Math.min(dp[1][n+1][0], dp[1][n+1][1]));
			out.flush();
		}
		out.close();
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值