ProblemA(HDU2048)
题目的主要问题就是求N张字条的错排数f(N)(分子)。
首先,如果前面N-1个人拿的都不是自己的票,即前N-1个人满足错排,现在又来了一个人,他手里拿的是自己的票。只要他把自己的票与其他N-1个人中的任意一个交换,就可以满足N个人的错排。这时有(N-1)*f(N-1)种方法。
同时,如果前N-1个人不满足错排,而第N个人把自己的票与其中一个人交换后恰好满足错排。
这种情况发生在原先N-1人中,N-2个人满足错排,有且仅有一个人拿的是自己的票,而第N个人恰好与他做了交换,这时候就满足了错排。
又因为前N-1个人中,每个人都有机会拿着自己的票。所以有N-1种交换的可能。故这时有(N-1)*f(N-2)种方法。
所以:f(N)=(N-1)*[f(N-1)+f(N-2)]
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long ll;
ll f[21], sum;
int main()
{
int i, t, n;
f[1] = 0; f[2] = 1;
for (i = 3; i<21; i++)
f[i] = (i - 1)*(f[i - 1] + f[i - 2]);
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
sum = 1;
for (i = 1; i <= n; i++)
sum *= i;
printf("%.2lf%%\n", f[n] * 100.0 / sum);
}
return 0;
}
ProblemB(HDU2049)
和上一题相似,加入了排组
N个新郎中M个错排是CMN=N!/(M!*(N-M)!)。即CMN=N!/M!/(N-M)!。
#include "cstdio"
#include "cstdlib"
typedef long long ll;
int main()
{
int n, m, i;
ll a[25], b[25];
a[0] = 1; a[1] = 1, a[2] = 2;
for (i = 3; i < 21; i++)
a[i] = a[i - 1] * i;
b[0] = 0; b[1] = 1; b[2] = 1, b[3] = 2;
for (i = 4; i < 21; i++)
b[i] = (i - 1)*(b[i - 1] + b[i - 2]);
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &m);
printf("%lld\n", a[n] / a[m] / a[n - m] * b[m]);
}
return 0;
}
ProblemC(HDU2068)
这题还是考查错排公式,加入了组合数运算。正确的数目用组合,错误的情况用错排。
错排公式为:F(1)=0;F(2)=1;F(n)=(n-1)*(F(n-1)+F(n-2));
#include<iostream>
#include<cstdio>
using namespace std;
__int64 a[13]={0,0,1};//只需要定义到a[13]即可,因为最大为25
void fun()
{
for(int i=3;i<=12;i++)
a[i]=(i-1)*(a[i-1]+a[i-2]); //错排
}
int cnm(int n,int m)
{
int s=1;
for(int i=1;i<=m;i++)
s=s*(n--)/i;
return s;
}
int main()
{
fun();
int n;
while(scanf("%d",&n)!=EOF&&n)
{
__int64 s=0; //__int64类型
if(n<=3)cout<<1<<endl; //小于4的情况不适合下面的情况
else
{
for(int i=(n+1)/2;i<=n-2;i++)//直到算到错排为2的情况 不存在错排为1的
{
s+=(cnm(n,i)*a[n-i]);
}
printf("%I64d\n",s+1);//错排为0的情况为1种 即全对的情况
}
}
return 0;
}
ProblemD(HDU2111)
这是一个经典的部分背包,由于物品都是可分割的,当然是用贪心的思想,单位价值越高越好。则先按单价排一次序,从头开始取,如果能装下就全部装进包里,否则将该物品分割开尽可能多的装。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef struct node
{
int q;
int m;
};
bool cmp(node x,node y)
{
return x.q>y.q;
}
node st[110];
int main()
{
int v,n;
while(1)
{
scanf("%d",&v);
if(v==0) break;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d%d",&st[i].q,&st[i].m);
}
sort(st,st+n,cmp);
int t=0;
int sum=0;
while(v && t<n)
{
if(v>=st[t].m)
{
v-=st[t].m;
sum+=st[t].q*st[t].m;
t++;
}
else
{
sum+=v*st[t].q;
v=0;
}
}
printf("%d\n",sum);
}
return 0;
}
ProblemE(POJ1700)
解释题意:N个人,只有一条船,每个人都有一个划船速度,要求让N个人过河,一次渡河包括两个人先过去,一个人再回来。每次时间取两个人之间用时最多的,一个人的时候的时间就是他自己所用的。求让N个人过完河所用的最短时间。
很明显又是一个经典——过河问题。用贪心的思想做。
先把每个人的速度从小到大排序,先考虑简单的情况,一个人,自己过去就可以了。两个人,取时间最长的那个,三个人,最优的是三个人所用时间之和(每让最快的那个单独划船回来)。
四个人或四个以上,就要考虑不同的决策把最慢和次慢的人渡河。有两种决策。
1.始终让最快的单独划船。 最快的和最慢的一起渡河,然后最快的划回来,最快和次慢的一起渡河,最快的再回来。所用时间为 s[n]+s[1] + s[n-1] +s[1]
2.让最快和次快的先过河,最快的划回来,接着最慢和次慢的过河,让次快的划回来。 这样所用时间为 s[2]+s[1]+s[n]+s[2]
两种决策中取时间最短的。
这里用非递归写的,思路相同也可以写出递归的形式,甚至可以把递归改成递推,有兴趣可以自己实践,大体部分不变。
#include <iostream>
#include <algorithm>
using namespace std;
int s[1002];
int n,time;
int main()
{
int t;cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>s[i];
sort(s+1,s+1+n);
time=0;
while(n)
{
if(n==1)//还剩下一个人,也就是一开始就有1个人渡河
{
time+=s[1];
break;
}
else if(n==2)
{
time+=s[2];
break;//别忘了break;
}
else if(n==3)
{
time+=s[1]+s[2]+s[3];//最优的方法为每次都让最快的单独划船
break;
}
else
{
time=time+min(s[n]+2*s[1]+s[n-1],2*s[2]+s[1]+s[n]);//比较,让最慢和次慢的两个人渡河有两种方法,取时间最短的那个
n-=2;
}
}
cout<<time<<endl;
}
return 0;
}