Codeforces 838E E. Convex Countour DP

题意:给一个凸包,保证任意三点不共线(这个条件好像没什么用)。让在凸包上求一条最长路,要求不能经过重复的点,且路径不能自交。点与点的距离按欧氏距离算。

点数为2500数量级,输入的点按顺时针顺序排好(就是不用求凸包了)。


解法:比赛的时候马上莽了一发贪心,枚举起点和两个起始方向,折线地走到终点,交了结果 wa 2,dp只想到了n^3的,于是就去弄第二题线段树了。

正解:易知该路径经过所有的点一定 不比 不经过所有的点差,于是用 dp[i][j][0/1] 表示凸包上 i 到 j 的点全部走完的情况,dp[i][j][0]表示最后落在i点,dp[i][j][1]表示最后落在j点,转移的话:如 dp[i][j][0],一定是:1) 从 j 走到 i;2) 从 i 的相邻右边点走到 i 两种情况之一转移过来的,取个max就行了。

代码写了一发dfs感觉有点慢,于是改成了for循环,好像还是不快。。

记忆化 DFS:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2555;
int n;
double dp[maxn][maxn][2],dist[maxn][maxn],x[maxn],y[maxn];
inline int pre(int x) {
    return (x+n-1)%n;
}
inline int nxt(int x) {
    return (x+1)%n;
}
inline void getdist(int i,int j) {
    dist[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
double dfs(int i,int j,int ty) {
    if (dp[i][j][ty]>=0)
        return dp[i][j][ty];
    int now=!ty?i:j,_pre=pre(i),_nxt=nxt(j);
    if (_pre==j)
        return 0;
    return dp[i][j][ty]=max(dfs(_pre,j,0)+dist[_pre][now],dfs(i,_nxt,1)+dist[now][_nxt]);
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;++i)
        scanf("%lf%lf",&x[i],&y[i]);
    for (int i=0;i<n;++i)
        for (int j=0;j<n;++j)
            getdist(i,j);
    for (int i=0;i<n;++i)
        for (int j=0;j<n;++j)
            for (int k=0;k<2;++k)
                dp[i][j][k]=-1;
    double res=0;
    for (int i=0;i<n;++i)
        res=max(res,dfs(i,i,0));
    printf("%.12f\n",res);
    return 0;
}

For 循环:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2555;
double dp[maxn][maxn][2],dist[maxn][maxn],x[maxn],y[maxn];
int n,pre[maxn],nxt[maxn];
inline int get_pre(int x) {
    return (x+n-1)%n;
}
inline int get_nxt(int x) {
    return (x+1)%n;
}
inline void getdist(int i,int j) {
    dist[i][j]=dist[j][i]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;++i) {
        scanf("%lf%lf",&x[i],&y[i]);
        pre[i]=get_pre(i),nxt[i]=get_nxt(i);
    }
    for (int i=0;i<n;++i)
        for (int j=0;j<=i;++j)
            getdist(i,j);
    for (int k=0;k<n-1;++k) {
        for (int i=0;i<n;++i) {
            int j=(i+k+1)%n;
            dp[i][j][0]=max(dp[nxt[i]][j][0]+dist[nxt[i]][i],dp[nxt[i]][j][1]+dist[i][j]);
            dp[i][j][1]=max(dp[i][pre[j]][0]+dist[i][j],dp[i][pre[j]][1]+dist[pre[j]][j]);
        }
    }
    double res=0;
    for (int i=0;i<n;++i)
        res=max(res,dp[i][(i+n-1)%n][0]);
    printf("%.12f\n",res);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值