POJ3276

/*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 
# 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 反面朝上记为0
	M=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=0)//更新sum
			 sum-=F[i-K+1];
	 }
	 return count;
}


附加:

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

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

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

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

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

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

就是标题中的那道题

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

# include 
# include 
# include 
# define N 40000
int main(){
long double A[3][N]={0},sum=0,MAX;
int i;
   srand(time(NULL));
   for(i=0;iA[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左右

POJ3276_第1张图片



你可能感兴趣的:(C语言小程序,编程题,编程,c语言,数学)