“蔚来杯“2022牛客暑期多校训练营5-A Don‘t Starve

"蔚来杯"2022牛客暑期多校训练营5-A Don’t Starve

原题题面:https://ac.nowcoder.com/acm/contest/33190/A

题目大意

在二维平面上,有 n ( 1 ≤ n ≤ 2000 ) n(1\le n\le 2000) n(1n2000)个位置有食物。从原点出发,每次直线前往其他任意一个有食物的位置收集食物。收集完后再次前往下一个点。每当离开一个有食物的点后,该点的食物就会刷新。并且每次的移动距离必须严格下降。
求最多可以收集到多少食物。

解题思路

如果每次都选符合条件的最长的,可能不是最优。(反例可以自己画)。考虑用 d p dp dp
为了防止最后的最佳答案不是从 0 0 0出发所得,可以进行反向 d p dp dp,输出 d p 0 dp_0 dp0的结果即可
d p i dp_i dpi为从第 i i i点出发所能得到的最大食品数,则转移方程为:
d p i = 1 + m a x ( d p j ) dp_i=1+max(dp_j) dpi=1+max(dpj)
为了保证边的长度由大到小,反向 d p dp dp前先由小到大排序。

代码实现

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e3+5;
ll n,dp[N],x[N],y[N],e[N],m;
template<class T>inline void read(T&x){
    char c,l=' ';
    while(!isdigit(c=getchar()))l=c;
    x=c^48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
    if(l=='-')x=-x;
}
struct node{
	ll x,y,w;
	node(ll x,ll y,ll w):x(x),y(y),w(w){};
};
vector<node>ve;
ll r(ll i,ll j){
	return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);\\能用整数就用整数
}
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(x[i]),read(y[i]);
		dp[i]=1,ve.push_back(node(0,i,r(0,i)));
	}
	for(ll i=2;i<=n;i++)
	for(ll j=1;j<i;j++){
		ve.push_back(node(i,j,r(i,j)));
	}
	sort(ve.begin(),ve.end(),[](node a,node b)->bool{return a.w<b.w;});//lambda表达式
	m=ve.size();
	for(int l=0,r;l<m;l=r){
		r=l;
		while(r<m&&ve[l].w==ve[r].w)r++;//防止前后边权相同
		for(int j=l;j<r;j++)e[ve[j].x]=dp[ve[j].x],e[ve[j].y]=dp[ve[j].y];
		for(int j=l,u,v;j<r;j++){
		u=ve[j].x,v=ve[j].y;
		e[u]=max(e[u],dp[v]+1);
		if(!u)continue;
		e[v]=max(e[v],dp[u]+1);
	   }
	   for(int j=l;j<r;j++)dp[ve[j].x]=e[ve[j].x],dp[ve[j].y]=e[ve[j].y];
    }
    cout<<dp[0]-1;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值