TopCoder SRM 566 Div 1 - Problem 1000 FencingPenguins

本文探讨了一道复杂的算法问题,涉及在平面坐标系中,围绕中心在原点的正多边形及其顶点,连接边以形成包含特定颜色企鹅的不相交圆圈。通过动态规划的方法,文章提供了一种解决方案,旨在计算满足特定条件的连边方案数量。

传送门:https://284914869.github.io/AEoj/566.html

题目简述:

平面上有中心在原点,一个点在(r,0)处的正n边形的n个顶点。
平面上还有m个企鹅,每个企鹅有一个位置和一个颜色,
现在要连一些边,使得每个点的度数都是0或2,
这样会构成若干个顶点不相交的圈,求满足以下条件的连边方案数:
1、每个圈里有至少一个企鹅;
2、任意两个圈不相交;
3、每种颜色的企鹅在同一个有限区域中;
4、每个企鹅在一个有限区域内;
n<=222,m<=50,r<=10^5, 
企鹅的颜色保证是大写字母或者小写字母,
坐标范围<=10^5,企鹅离任一条n个点连的边的距离>10^-6

思路:

很显然这题要用dp。考虑怎么用dp做。

 

如图所示

我们先定义“特殊”多边形为有>=3条边的多边形或一个点。如图有6个多边形(包括点E)。

这样所有的点就都在多边形上了。

我们选任意一个点作为起点。例如D。

D所在的多边形为DGHI。这样弧DG,GH,HI,ID上的点形成的多边形就是互不干扰的了。

所以初步构思,可以从若干个小弧的状态推到一个大弧的状态。(我可能说不清楚具体过程,感性理解)

还有一个问题就是同颜色的企鹅在同一个多边形内。

这个限制可以转换为,所有的边两侧不存在同颜色的企鹅,且所有企鹅都不在多边形外部。

那么写起来就更方便了。

代码:

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <string>
  4 #include <vector>
  5 #include <cstring>
  6 #include <iostream>
  7 #include <algorithm>
  8 using namespace std;
  9 #define _CLASSNAME_ FencingPenguins
 10 #define _METHODNAME_ countWays
 11 #define _RC_ int
 12 #define _METHODPARMS_ int _n, int _r, vector<int> _x, vector<int> _y, string _c
 13 #define ref(i,x,y)for(int i=x;i<=y;++i)
 14 #define def(i,x,y)for(int i=x;i>=y;--i)
 15 #define reset(a)memset(a,0,sizeof a)
 16 const double pi=acos(-1);
 17 const int mod=100007;
 18 struct point{
 19     double x,y;
 20     point(){x=0;y=0;}
 21     point(double X,double Y){x=X,y=Y;}
 22 };
 23 point operator -(point a,point b){ return point(a.x-b.x,a.y-b.y); }
 24 double operator ^(point a,point b){ return a.y*b.x-a.x*b.y; }
 25 int n,m,r,ans;
 26 int p_c[51],fg[52],sum[445][445];
 27 point O[301],p[51];
 28 bool ok[445][445];
 29 void inc(int&a,int b){a+=b;if(a>=mod)a-=mod;}
 30 int mul(int a,int b){return 1LL*a*b%mod;}
 31 int chd(char c){if(c>='a'&&c<='z')return c-'a';else return c-'A'+26;}
 32 void work1(){
 33     reset(ok); reset(sum); ans=0;
 34     ref(i,0,n-1) O[i]=point(r*cos(2*pi*i/n),r*sin(2*pi*i/n));
 35     ref(i,0,n-1)ok[i][i]=1;
 36     ref(i,0,n-1)ref(j,i+1,n-1){
 37         ref(k,0,51)fg[k]=0;
 38         ref(k,1,m){
 39             int C=p_c[k]; double s=(O[j]-O[i])^(p[k]-O[i]); 
 40             if(!fg[C])fg[C]=(s>0)+1;else if(fg[C]!=-1) if(fg[C]!=(s>0)+1)fg[C]=-1;
 41             sum[i][j]+=(s>0);
 42         }
 43         bool flag=1; ref(k,0,51)flag=flag&&(fg[k]>=0);    
 44         ok[i][j]=ok[j][i]=flag; sum[j][i]=m-sum[i][j];
 45     }
 46     ref(i,0,n-1)ref(j,0,n-1) sum[i+n][j]=sum[i][j+n]=sum[i+n][j+n]=sum[i][j];
 47     ref(i,0,n-1)ref(j,0,n-1) ok[i+n][j]=ok[i][j+n]=ok[i+n][j+n]=ok[i][j];
 48 }
 49 int dp1[445][445][2],dp2[445][445]; bool tri[223][445][445];
 50 void work2()
 51 {
 52     reset(dp1); reset(dp2); reset(tri);
 53     ref(i,0,n-1)if(sum[i][i+1])return;
 54     ref(i,0,n-1)ref(j,i,i+n-1)ref(k,i+1,j-1)tri[i][k][j]=(sum[i][j]-sum[i][k]-sum[k][j]>0);
 55     ref(i,0,n*2-1)dp1[i][i][1]=1,dp2[i][i]=1;
 56     ref(len,1,n-1){
 57         ref(i,0,n-1){
 58             int j=i+len;
 59             ref(k,i,j-1) if(!tri[i][k+1][j]&&!tri[i][j][j+1])
 60                 if(ok[k+1][j])inc(dp2[i][j],mul(dp2[i][k],dp1[k+1][j][1]));
 61             inc(dp1[i][j][0],dp2[i][j-1]);
 62             ref(k,i+1,j-1)ref(d,0,1) 
 63                 inc(dp1[i][j][d|tri[i][k][j]],mul(dp1[i][k][d],dp2[k][j-1]));
 64             if(j<n)dp1[i+n][j+n][0]=dp1[i][j][0],dp1[i+n][j+n][1]=dp1[i][j][1];
 65             if(j<n)dp2[i+n][j+n]=dp2[i][j];
 66         }
 67     }
 68     if(!tri[n-1][n][n+1])ans=dp2[0][n-1];
 69     ref(i,2,n-1)if(ok[0][i])inc(ans,mul(dp1[0][i][1],dp2[i][n-1]));
 70 }
 71 class _CLASSNAME_{
 72 public:
 73     _RC_ _METHODNAME_(_METHODPARMS_)
 74     {
 75         n=_n; m=_x.size(); r=_r;
 76         ref(i,1,m)p[i].x=_x[i-1],p[i].y=_y[i-1];
 77         ref(i,1,m)p_c[i]=chd(_c[i-1]);
 78         work1();work2();
 79         return _RC_(ans);
 80     }
 81     
 82 // BEGIN CUT HERE
 83     public:
 84     void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); if ((Case == -1) || (Case == 4)) test_case_4(); if ((Case == -1) || (Case == 5)) test_case_5(); if ((Case == -1) || (Case == 6)) test_case_6(); if ((Case == -1) || (Case == 7)) test_case_7(); }
 85     private:
 86     //template <typename T> string print_array(const vector<T> &V) { ostringstream os; os << "{ "; for (typename vector<T>::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << '\"' << *iter << "\","; os << " }"; return os.str(); }
 87     void verify_case(int Case, const int &Expected, const int &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << '\"' << endl; cerr << "\tReceived: \"" << Received << '\"' << endl; } }
 88     void test_case_0() { int Arg0 = 4; int Arg1 = 10; int Arr2[] = {2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {1}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "R"; int Arg5 = 3; verify_case(0, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 89     void test_case_1() { int Arg0 = 4; int Arg1 = 10; int Arr2[] = {2,-2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {1,-1}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "RR"; int Arg5 = 1; verify_case(1, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 90     void test_case_2() { int Arg0 = 8; int Arg1 = 10; int Arr2[] = {8,-8,-8,8}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {1,-1,1,-1}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "BBBB"; int Arg5 = 25; verify_case(2, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 91     void test_case_3() { int Arg0 = 8; int Arg1 = 10; int Arr2[] = {8,-8,-8,8}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {1,-1,1,-1}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "RGBY"; int Arg5 = 50; verify_case(3, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 92     void test_case_4() { int Arg0 = 6; int Arg1 = 5; int Arr2[] = {0,0}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {-4,4}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "rB"; int Arg5 = 6; verify_case(4, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 93     void test_case_5() { int Arg0 = 3; int Arg1 = 5; int Arr2[] = {4}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {3}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "y"; int Arg5 = 0; verify_case(5, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 94     void test_case_6() { int Arg0 = 200; int Arg1 = 100000; int Arr2[] = {1020,30203,2302,203,-12321,-21332,8823,-2133,2323}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {-123,2131,4434,1223,43434,2323,4343,-213,-2325}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "YBYBWWBRr"; int Arg5 = 27547; verify_case(6, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 95     void test_case_7() { int Arg0 = 222; int Arg1 = 1713; int Arr2[] = {1091, 243, 505, 510, 869, 700, 790, 70, 260, 177, 273, 9, 43, -83, -163, -79, -352, -231, -663, -8, -768, -128, -768, -372, -577, -895, -994, -564, -509, -199, -833, -391, -445, -276, -115, -133, -166, -37, 65, 170, 251, 389, 23, 194, 130, 629, 477, 401, 1080, 36}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arr3[] = {4, 147, 265, 52, 302, 548, 183, 302, 439, 176, 167, 287, 677, 826, 156, 550, 758, 129, 254, 308, 595, 445, 310, 230, 99, -3, -85, -113, -321, -99, -134, -98, -747, -615, -216, -760, -891, -248, -783, -396, -586, -866, -479, -50, -731, -50, -195, -153, -23, -9}; vector <int> Arg3(Arr3, Arr3 + (sizeof(Arr3) / sizeof(Arr3[0]))); string Arg4 = "xidylnzmnsolwfyhgjaegnwgazjbdmfwaldsmqxpowtianiesx"; int Arg5 = 64346; verify_case(7, Arg5, countWays(Arg0, Arg1, Arg2, Arg3, Arg4)); }
 96 // END CUT HERE
 97 
 98 };
 99 // BEGIN CUT HERE
100 int main()
101 {
102     _CLASSNAME_ user;
103     user.run_test(-1);
104 }
105 // END CUT HERE

 

转载于:https://www.cnblogs.com/Blog-of-Eden/p/7852426.html

状压DP超详细教程:从入门到精通 状压DP(状态压缩动态规划)是一种非常实用的算法技巧,特别适合处理状态可以用二进制表示的问题。下面我将用最详细、最系统的方式讲解这个技术,保证你能彻底理解。 一、状压DP的本质 1.1 什么是状态压缩? 状态压缩的核心思想是:用二进制位来表示某种状态。比如: 有5个灯泡:可以用5位二进制数表示它们的开关状态 10101表示第1、3、5个灯亮,2、4灭 有8个任务是否完成:可以用8位二进制数表示 11001001表示第1、2、5、8个任务已完成 1.2 为什么需要压缩状态? 传统DP在表示某些状态时会遇到困难。例如: 棋盘放置问题:要记录哪些格子被占用 任务分配问题:要记录哪些任务已被分配 路径问题:要记录哪些点已经访问过 如果用传统数组表示,可能需要多维数组,空间复杂度爆炸。而用二进制压缩,一个整数就能表示复杂的状态。 二、状压DP的三大组成部分 2.1 状态表示 用一个整数的二进制形式表示状态: 每一位代表一个元素的状态(选中/未选中,存在/不存在等) 整数范围:0到2ⁿ-1(n是元素个数) 示例:3个物品的选择状态 000(0):都没选 0011):选第1个 010(2):选第2个 011(3):选第1、2个 ... 111(7):全选 2.2 状态转移 定义如何从一个状态转移到另一个状态,通常包括: 检查当前状态的某些位 根据条件修改某些位 生成新状态 2.3 DP数组设计 dp[state]或dp[state][i],其中: state是压缩后的状态 i可能是附加信息(如当前位置、已选数量等) 三、必须精通的位运算技巧 3.1 基本操作 操作 代码表示 示例(假设8位二进制) 设置第i位为1 `state (1 << i)` `0010 (1<<2) → 0110` 设置第i位为0 state & ~(1 << i) 0110 & ~(1<<2) → 0010 切换第i位 state ^ (1 << i) 0110 ^ (1<<2) → 0010 检查第i位是否为1 (state >> i) & 1 (0110 >> 2) & 11 3.2 高级技巧 枚举所有子集: cpp for(int subset = state; subset; subset = (subset-1)&state){ // 处理subset } 最低位的1: cpp int lowbit = x & -x; 统计1的个数: cpp int count = __builtin_popcount(state); // GCC内置函数 六、状压DP的优化技巧 6.1 预处理合法状态 很多问题中,大部分状态是不合法的,可以预先筛选: cpp vector<int> valid_states; for (int state = 0; state < (1 << n); ++state) { if (check(state)) { // 检查state是否合法 valid_states.push_back(state); } } 6.2 滚动数组优化 当状态只依赖前一个阶段时,可以节省空间: cpp vector<vector<int>> dp(2, vector<int>(size)); // 只保留当前和上一个状态 int now = 0, prev = 1; for (int i = 1; i <= n; ++i) { swap(now, prev); for (auto& state : valid_states) { dp[now][state] = 0; // 清空当前状态 // 状态转移... } } 6.3 记忆化搜索实现 有时递归形式更直观: cpp int memo[1<<20][20]; // 记忆化数组 int dfs(int state, int u) { if (memo[state][u] != -1) return memo[state][u]; // 递归处理... return memo[state][u] = res; } 七、常见问题与调试技巧 7.1 常见错误 位运算优先级:总是加括号,如(state & (1 << i)) 数组越界:状态数是2ⁿ,不是n 初始状态设置错误:比如TSP中dp[1][0] = 0 边界条件处理不当:如全选状态是(1<<n)-1,不是1<<n 7.2 调试建议 打印中间状态:将二进制状态转换为可视化的形式 cpp void printState(int state, int n) { for (int i = n-1; i >= 0; --i) cout << ((state >> i) & 1); cout << endl; } 从小规模测试用例开始(如n=3,4) 使用assert检查关键假设 八、学习路线建议 初级阶段: 练习基本位操作 解决简单状压问题(如LeetCode 464、526题) 中级阶段: 掌握经典模型(TSP、棋盘覆盖) 学习优化技巧(预处理、滚动数组) 高级阶段: 处理高维状压(如需要同时压缩多个状态) 结合其他算法(如BFS、双指针) 九、实战练习题目推荐 入门题: LeetCode 78. Subsets(理解状态表示) LeetCode 464. Can I Win(简单状压DP) 中等题: LeetCode 526. Beautiful Arrangement LeetCode 691. Stickers to Spell Word 经典题: POJ 2411. Mondriaan's Dream(棋盘覆盖) HDU 3001. Travelling(三进制状压) 挑战题: Codeforces 8C. Looking for Order Topcoder SRM 556 Div1 1000. LeftRightDigitsGame2 记住,掌握状压DP的关键在于: 彻底理解二进制状态表示 熟练运用位运算 通过大量练习培养直觉 希望这份超详细的教程能帮助你彻底掌握状压DP!如果还有任何不明白的地方,可以针对具体问题继续深入探讨。 请帮我转成markdown语法输出,谢谢
08-13
代码转载自:https://pan.quark.cn/s/a4b39357ea24 本文重点阐述了利用 LabVIEW 软件构建的锁相放大器的设计方案及其具体实施流程,并探讨了该设备在声波相位差定位系统中的实际运用情况。 锁相放大器作为一项基础测量技术,其核心功能在于能够精确锁定微弱信号的频率参数并完成相关测量工作。 在采用 LabVIEW 软件开发的锁相放大器系统中,通过计算测量信号与两条参考信号之间的互相关函数,实现对微弱信号的频率锁定,同时输出被测信号的幅值信息。 虚拟仪器技术是一种基于计算机硬件平台的仪器系统,其显著特征在于用户可以根据实际需求自主设计仪器功能,配备虚拟化操作界面,并将测试功能完全由专用软件程序实现。 虚拟仪器系统的基本架构主要由计算机主机、专用软件程序以及硬件接口模块等核心部件构成。 虚拟仪器最突出的优势在于其功能完全取决于软件编程,用户可以根据具体应用场景灵活调整系统功能参数。 在基于 LabVIEW 软件开发的锁相放大器系统中,主要运用 LabVIEW 软件平台完成锁相放大器功能的整体设计。 LabVIEW 作为一个图形化编程环境,能够高效地完成虚拟仪器的开发工作。 借助 LabVIEW 软件,可以快速构建锁相放大器的用户操作界面,并且可以根据实际需求进行灵活调整和功能扩展。 锁相放大器系统的关键构成要素包括测量信号输入通道、参考信号输入通道、频率锁定处理单元以及信号幅值输出单元。 测量信号是系统需要检测的对象,参考信号则用于引导系统完成对测量信号的频率锁定。 频率锁定处理单元负责实现测量信号的锁定功能,信号幅值输出单元则负责输出被测信号的幅值大小。 在锁相放大器的实际实现过程中,系统采用了双路参考信号输入方案来锁定测量信号。 通过分析两路参考信号之间的相...
边缘计算环境中基于启发式算法的深度神经网络卸载策略(Matlab代码实现)内容概要:本文介绍了在边缘计算环境中,利用启发式算法实现深度神经网络任务卸载的策略,并提供了相应的Matlab代码实现。文章重点探讨了如何通过合理的任务划分与调度,将深度神经网络的计算任务高效地卸载到边缘服务器,从而降低终端设备的计算负担、减少延迟并提高整体系统效率。文中涵盖了问题建模、启发式算法设计(如贪心策略、遗传算法、粒子群优化等可能的候选方法)、性能评估指标(如能耗、延迟、资源利用率)以及仿真实验结果分析等内容,旨在为边缘智能计算中的模型推理优化提供可行的技术路径。; 适合人群:具备一定编程基础,熟悉Matlab工具,从事边缘计算、人工智能、物联网或智能系统优化方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①研究深度神经网络在资源受限设备上的部署与优化;②探索边缘计算环境下的任务卸载机制与算法设计;③通过Matlab仿真验证不同启发式算法在实际场景中的性能表现,优化系统延迟与能耗。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,重点关注算法实现细节与仿真参数设置,同时可尝试复现并对比不同启发式算法的效果,以深入理解边缘计算中DNN卸载的核心挑战与解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值