eightgon题解,思维.

Eightgon

https://ac.nowcoder.com/acm/contest/13926/D

Eightgon题解:
题意:
要求在点的集合里面选择八个点建造,的每个点上都有一个特殊机器,使粒子速度向左偏转45°.问有几种建造**的方式.
图片说明
思路:
题目就是要我们求可以构成相邻两条边夹角是135°的八边形的数量.
思路:
用dp[i][j][k]来表示从i点开始花费了k条边到达j点的路径数量,
有递推式
图片说明
等式后面的∑b代表的是符合条件:以b为顶点,c在其合法的射线上.( 合法的射线指的是射线bc或平形于x轴,或垂直x轴,或于x轴夹角为45°+/-k
90°(k>0) ).
dp[i][i][8]即为i点作为顶点之一可以构成八边形的数量,但这样不仅重复,还会超时,时间复杂度是O(n^3).
优化:将所有点按八个方向分别排序,依次遍历八个方向,用dp[i][j]记录在该方向上排j点之前的点花费i条线到达j点的方案数.
递推式

    dp[d+1][j]=dp[d][last] + dp[d+1][last];
    //递推式的第一项表示从last点到点j,粒子改变了方向的方案数;第二项表示从last到j点粒子没有改变方向的方案数.

每遍历完一个方向后就加上dp[8][start] (start为起点.)这样也不会有重复计算八边形的数量.
代码:

#include<algorithm>
#include<iostream>
#include<vector>

// 时间复杂度: O(n^2)
// 该题主要思维和学习处理数据的方法 
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;

const int maxn = 5000;
int n, x[maxn], y[maxn];
ll dp[9][maxn];//后面有该数组的定义.

//注意这个函数的返回类型,是pair<int,int>.
//在这里可以理解为返回两个数分别是ordering(i,d).first和ordring(i,d).second
//主要用于之后的排序 
ii ordering(int i, int d) 
{
    if (d==0) return ii( y[i],  x[i]);
    if (d==2) return ii(-x[i],  y[i]);
    if (d==4) return ii(-y[i], -x[i]);
    if (d==6) return ii( x[i], -y[i]);

    if (d==1) return ii(-x[i] + y[i],  x[i] + y[i]);
    if (d==3) return ii(-x[i] - y[i], -x[i] + y[i]);
    if (d==5) return ii( x[i] - y[i], -x[i] - y[i]);
    if (d==7) return ii( x[i] + y[i],  x[i] - y[i]);
}

int main() {
    cin >> n;
    for (int i=0; i<n; i++) {
        cin >> x[i] >> y[i];
    }

    vector<int> order[8]; 
    for (int d=0; d<8; d++) {
        order[d] = vector<int>(n);
        for (int i=0; i<n; i++) {
            order[d][i] = i;
        }
        sort(begin(order[d]), end(order[d]), [&](int a, int b) -> bool { return ordering(a,d) < ordering(b,d);} );
        //按粒子运动方向进行排序 
        //[&](int a, int b) -> bool { return ordering(a,d) < ordering(b,d); 相当于自定义的排序函数函数,
        //在这里除了方便没什么别的用处. 
    }

    ll res = 0;
    for (int i=0; i<n; i++) //将每个点都作为起点查找一次 
    {
        for (int j=0; j<n; j++) // 初始化
        {
            dp[0][j] = 0;
        }
        dp[0][i] = 1; 
        //dp[d][j]表示粒子从i点出发使用了d条边到达j点的方案数.
        for(int d=0; d < 8; d++)//遍历8个方向,计算dp[ ][ ] 
        {
            int last = -1;
            for (int j : order[d])
            {
                if(last==-1)
                    dp[d+1][j]=0;
                if (ordering(j, d).first != ordering(last, d).first) 
                    dp[d+1][j]=0;
                else 
                    dp[d+1][j]=dp[d][last] + dp[d+1][last];
                last = j;
            }
        }
        //cout<<" "<<dp[8][i]<<endl; 
        res += dp[8][i];//使用了八条边后回到起点的方案数. 
    }
    cout<<res<<endl;
}

有错误欢迎指正.QAQ

全部评论
大佬太强了,谢谢题解
2 回复 分享
发布于 2021-05-06 19:35

相关推荐

评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务