ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

2021“MINIEYE杯”中国大学生算法设计超级联赛(10)1003. / HDU7079 Pty loves lines(bitset优化DP)

2021-08-20 02:01:33  阅读:226  来源: 互联网

标签:10 int lines 条线 bitset loves ans dp


Problem Description

You are required to put n straight lines on a plane, guaranteeing no three lines share a common point and no lines are coincident. They will form some intersections, please output all possible numbers of intersections.

Input

The first line, an integer T(1 ≤ T ≤ 5) - the number of test cases.

Following T lines, an positive integer n(1 ≤ n ≤ 700) - the number of lines.

Output

Several lines, each line several numbers separated by a blank space - all possible numbers of intersections.

Sample Input

2
3
5

Sample Output

0 2 3
0 4 6 7 8 9 10

这个题题意非常短,问的是给定n条直线,问在没有三线共点等情况下能产生多少种不同的交点数。

这个题可以看做HDU1466的加强版。原题给的数据范围是n <= 20,那么就可以用普通的dp求解。如果手玩几个样例,例如n = 5,会发现一开始如果五条线平行的话交点为0,四条线平行一条线与这四条平行线相交的话交点为4,三条线平行,另外两条线互相平行或相交的话交点数为6或者7...可以看出来平行线的存在能够将问题规模缩小。因为如果x条线相互平行,另外y条线任意(但不与这x条线平行)的话,总的交点数就是y条线自己产生的交点数 + x * y,原因就是y条线中的每条线都与x条平行线产生x个交点。

这样就可以进行dp了,设\(dp[i, j]\)为用i条直线产生j个交点是否可行,k为i条线中相互平行的线的数量,则转移方程为\(dp[i, j] \ |= \ dp[i - k, j - (i - k) \times k]\)。其中i - k就是任意的线的数量,(i - k) * k就是任意的线与平行线产生的交点数。这样就可以轻松通过HDU1466这道题了。

#include <bits/stdc++.h>
using namespace std;
const int N = 25;
const int NN = N * N / 2;//交点数最多有这么多
bool dp[N + 5][NN + 5];//dp[i][j]表示用i条直线凑出来j个点是否可行
void init() {
    for(int i = 0; i < N; i++) dp[i][0] = 1;
    for(int i = 1; i <= N; i++) {
        for(int j = 1; j <= NN; j++) {
            for(int k = 0; k < i; k++) {//k条直线平行
                if(j - (i - k) * k >= 0) dp[i][j] = dp[i][j] | dp[i - k][j - (i - k) * k];
            }
        }
    }
}
void solve(int n) {
    vector<int> ans;
    for(int i = 0; i < NN; i++) {
        if(dp[n][i]) ans.push_back(i);
    }
    for(int i = 0; i < ans.size(); i++) {
        if(i != ans.size() - 1) cout << ans[i] << " ";
        else cout << ans[i] << endl;//注意行末空格
    }
}
int main () {
    cin.tie(0);
    ios::sync_with_stdio(false);
    init();
    int n;
    while(cin >> n) {
        solve(n);
    }
}

然而对于比赛的这道题,n的范围陡然增加到了700,上述代码的时间复杂度为\(O(n^4)\),显然时间上无法通过,而且有爆内存的风险。那么就要考虑进行优化,一种可行的方案是使用bitset进行优化。观察上面代码,第二重循环实际上直接贡献了\(n^2\)的复杂度,那么借助bitset,我们可以把它优化成\(O(\frac{n^2}{w})\)。具体来说,对于每一个i维护一个bitset,bitset的大小为\(\frac{n\times (n - 1)}{2}\),在循环里直接省略掉原先的第二重循环,同时把转移方程的写法改为dp[i] = dp[i] | dp[i - k] << ((i - k) * k);这里相当于整体进行或操作而不是单独进行遍历,借助bitset的玄学操作可以带来不小的提升。因为左移会在低位补0,因此也不用像上面代码一样担心越界的问题。

#include <bits/stdc++.h>
using namespace std;
const int N = 700;
const int NN = N * N / 2;
bitset<NN + 5> dp[N + 5];
void init() {
    for(int i = 0; i < N; i++) dp[i][0] = 1;
    for(int i = 1; i <= N; i++) {
        for(int k = 0; k < i; k++) {
            dp[i] = dp[i] | dp[i - k] << ((i - k) * k);
        }
    }
}
void solve(int n) {
    vector<int> ans;
    for(int i = 0; i < NN; i++) {
        if(dp[n][i]) ans.push_back(i);
    }
    for(int i = 0; i < ans.size(); i++) {
        if(i != ans.size() - 1) cout << ans[i] << " ";
        else cout << ans[i] << endl;
    }
}
int main () {
    cin.tie(0);
    ios::sync_with_stdio(false);
    init();
    int n;
    while(cin >> n) {
        solve(n);
    }
}

然而本地跑一下会发现init()函数执行的时间还是非常长(需要好几秒才能进行查询),瓶颈在于bitset开的实际上非常大。这时候就需要继续进行优化。比赛的时候发现当输出了若干个数以后,后面的数都是连续的,通过对拍程序测试一下n = 20的时候100以后基本都是连续的,猜想可能是大约5倍以上后面就都是连续的了(然而是错的),题解给的界限是31500,这样的话bitset实际上只需要开到这个上界就足矣,i大于上届的话直接输出即可,这样就能通过本题了。

#include <bits/stdc++.h>
using namespace std;
const int N = 700;
const int NN = N * N / 2;
#define MX 35000
bitset<MX> dp[N + 5];//dp[i][j]表示用i条直线凑出来j个点是否可行
void init() {
    for(int i = 0; i <= N; i++) dp[i][0] = 1;
    for(int i = 1; i <= N; i++) {
        for(int k = 0; k <= i; k++) {
            dp[i] = dp[i] | (dp[i - k] << ((i - k) * k));
        }
    }
}
void solve(int n) {
    vector<int> ans;
    int lim = min(MX - 1, n * (n - 1) / 2);
    for(int i = 0; i <= lim; i++) {
        if(dp[n][i]) ans.push_back(i);
    }
    for(int i = lim + 1; i <= n * (n - 1) / 2; i++) ans.push_back(i);
    for(int i = 0; i < ans.size(); i++) {
        if(i != ans.size() - 1) cout << ans[i] << " ";
        else cout << ans[i] << endl;
    }
}
int main () {
    cin.tie(0);
    ios::sync_with_stdio(false);
    init();
    int t;
    cin >> t;
    while(t--) {
        int n;
        cin >> n;
        solve(n);
    }
}

标签:10,int,lines,条线,bitset,loves,ans,dp
来源: https://www.cnblogs.com/lipoicyclic/p/15164739.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有