/*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 反面朝上记为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<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 40000
int 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左右