POJ3276

来源:互联网 发布:次声波软件 编辑:程序博客网 时间:2024/06/06 05:35

/*POJ3276有N张牌 正面朝上记为1 反面朝上记为0 每张牌都是正面或反面朝上设置一个数K 当每次翻牌时 翻K张连续的牌 请求出 为了让所有的牌反面朝上的最小操作次数M和对应的K  N∈[1,5000]  EG:输入N=7   1101011  输出 K=3 N=3  (先反转1--3牌 再反转3--5  再反转5--7)解:如果把牌的方向01便历搜索则需要2^N次方 N比较大的时候是无法求解的 需要改变方式  首先 对相同的一个区间进行多次反转是多余的  通过观察 还会发现 交换区间的反转顺序是对结果没有影响的  因为 对每个牌来说 一个个体翻多少次是固定的 且只有0--1两种状态  就像上面的例子 也可以 先反转3--5牌 再反转1--3  再反转5--7  一样的效果  于是 我们想办法求出需要反转的区间的集合会方便一些   区间!!!  先考虑最左边的牌 无论K为何值 包含这张牌的区间只有一个,如果这张牌正面 我们就会知道  包含这张牌并且以这张牌为首的区间是不需要反转的。  否则进行反转 当这张牌变为1之后 再考虑右侧区间 问题规模减少了1    不断地重复下去 就可以不用一一搜索求反转次数了  对于K 并不需要对所有的K都求一遍,这个地方有待化简 也是降低时间复杂度的重要地方  设F[i]=对区间[i,i+K-1]进行了一次反转 翻转了的话为1 没有反转记为0  先考虑一下下面的问题   如果K=3     下方的数轴中 最左边的牌为数轴上的1     Q是第五张牌    那么包含连续的K=3张牌 且包含Q的区间F[?] 有 F[3,3+3-1]  F[4,4+3-1]   F[5,5+3-1] 三种  其中这三个F[ ]中的前两个和Q之前的牌的状况有关 而F[5,5+3-1]是与前两个有关的  因为F[3,3+3-1]  F[4,4+3-1] 的反转与否 都决定了F[5,5+3-1]是否要进行反转                     Q |____|____|____|____|____|____|____|_________>F 1    2    3    4    5    6    7    8   回到F  对于第i张牌 ______________________________________|   i-1                               ||    ∑   F[j]     j范围 i-K+1 --> i-1||  j=i-k+1                            ||_____________________________________|如果∑F[j]为奇数 则这张牌的方向与最开始的方向是相反的否则方向没变又有________________________________________________|     i          i-1                           ||    ∑   F[j] =  ∑ F[j]+F[i]-F[i-K+1]        ||  j=(i+1)-k+1   j=i-K+1                       ||______________________________________________|这样就可以用常数的时间求出来K 的情况下需要反转的次数动态规划   代码:*/# include <stdio.h># define MAX 5001   int KKK(char S[],int N,int K);//返回K的情况下的反转次数int main(){    int K,M,N,k,m;char S[MAX];    scanf("%d ",&N);gets(S);//正面朝上记为1 反面朝上记为0M=N;    for(K=1;K<=N;K++)//遍历{    m=KKK(S,N,K);if(m>=0&&M>m)//如果当前K满足条件 且次数少{  M=m;  k=K;}}printf("K=%d M=%d\n",k,M);    return 0;}int KKK(char S[],int N,int K)//返回K的情况下的反转次数{ int F[MAX]={0},sum=0,count=0,i;//sum动态计算∑F[i]     for(i=0;i<=N-K;i++)//计算区间F[i,i-K+1] { if((S[i]-48+sum)%2)//如果前面的牌经过了 "S[i]"+sum次转变+此牌原本的状态 后 此牌现状还是在正面 {     count++;//此时加一反转 F[i]=1;//做标记 }     sum+=F[i];//更新反转次数         if(i-K+1>=0)//为了防止数组越界 因为i-K+1不一定>=0 sum-=F[i-K+1]; } for(i=N-K+1;i<N;i++)//检测前N-K张牌反转后 剩下的牌中是否有不符合条件的 { if((S[i]-48+sum)%2)//一旦检测到 说明这种情况下不符合 return -1; if(i-K+1>=0)//更新sum sum-=F[i-K+1]; } return count;}


附加:

▲ABC中∠A∈[60°,90°],则∠A取得最大角的概率为?计算机求概率

我很喜欢数学,在高中的时候想过很多题  也对一些数学故事感兴趣 记得当时看正17边形的证明和画法看了整整两天才看明白。。。然后自己画了几个才满意。

当时在上高中的时候,有一道题是这样说的:

有一根细长木棒长为L,随机切两下,则能组成一个三角形的概率为多少?

解法使用的 设边A=X,边B=Y根据三角形的任意两边之和大于第三边的不等式画图       解得P=0.25

我大二学概率论时突然间想出了一道题,可是却没办法解答。

就是标题中的那道题

我问了老师和学长 自己也一直在想  然后我就用了电脑的方法编了个程序:

# include <time.h># include <stdio.h># include <stdlib.h># define N 40000int main(){long double A[3][N]={0},sum=0,MAX;int i;   srand(time(NULL));   for(i=0;i<N;i++)   {      A[0][i]=rand()%30+60+0.000000001*(rand()%100000000); A[1][i]=rand()%(180-int(A[0][i]))+0.000000001*(rand()%100000000);      MAX=(A[0][i]>A[1][i]?A[0][i]:A[1][i]); A[2][i]=180.0-A[1][i]-A[0][i]; MAX=(A[2][i]>=MAX?A[2][i]:MAX); if(MAX==A[0][i]) sum++;   }   printf("%lf",sum*1.0/N);   return 0;}


毕竟使用计算机的随机数模拟的  大部分结果都是在0.439左右

经过一星期的思考  我的到了这个问题的解答案是3/7≈0.428:

如果程序没问题的话结果也应该是0.428左右  不清楚为什么会多0.01左右




1 0
原创粉丝点击