https://vjudge.net/contest/334111#overview
题意 : 给定n个点,求两个点间最小距离;n <= 10000;
n ^ 2 是肯定过不了的,考虑分治;
1、将集合一分为二,求左半边最小距离,右半边最小距离,返回两者较小值;
2、以较小值为半径,以中心点为圆心画圆,寻找左右两边在圆内的点,暴力求距离然后更新答案。
很巧妙,很经典;
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int MAXN = 10000 + 5; int n; struct hh {double x ,y;}a[MAXN]; double calc(hh a,hh b) {return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y,2) ); } bool cmp(hh a,hh b) {return a.x < b.x;} double D(int l,int r) { double ans = 2147483647.0; int mid = l + r >> 1; if(r - l <= 1) { ans = calc(a[l],a[r]); if(l != r) return ans; else return 2147483647.0; } ans = min(D(l , mid),D(mid + 1 , r)); int l0 = mid - 1, r0 = mid + 1; while(a[l0].x - a[mid].x < ans && l0 >= l) l0 --; while(a[r0].x - a[mid].x < ans && r0 <= r) r0 ++; for(int i = l0 + 1;i <= r0 - 1 ;i ++) for(int j = i + 1;j <= r0 - 1;j ++) ans = min(ans ,calc(a[i] ,a[j])); return ans; } void solve() { for(int i = 1;i <= n;i ++) scanf("%lf%lf",&a[i].x,&a[i].y); sort(a + 1,a + n + 1,cmp); double ans = D(1,n); if(ans >= 10000.0) cout << "INFINITY" << endl; else printf("%.4lf\n",ans); return; } int main() { while(cin >> n && n) solve(); return 0; }
https://blog.csdn.net/qq_40907279/article/details/78708856这个博客讲的很清晰。
先考虑直线分割,发现f(n) = f(n - 1) + n,再考虑将直线变成折线。
直线变成折线时,如图;
f(n) = f(n - 1) + n;
因此 F(n) = f(2 * n) - 2 * n;
然后化简为 n * n * 2 - (n - 1)
平面分割问题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,T;
int main()
{
cin >> T;
while(T --)
{
cin >> n;
cout << n * n * 2 - (n - 1) << "\n";
}
}
错排:n个人写了n封信送给其他人,问有多少种赠送方案(即每个人不能把信给自己)
错排公式为 f(n) = (n - 1)[f(n - 1) + f(n - 2)]
f(1) = 0, f(2) = 1;
第k个人赠送方案为(n - 1)种,假设他给了第m个人;
分两种情况:
1、第m个人把信给了第k个,则此时方案数为f(n - 2);
2、第m个人没有把信给第k个人,则相当于m是k(m不能给k,k不能给k,两者等价),则此时方案数为f(n - 1);
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
long long a[25];
long long T,n,m;
long long c(long long x,long long y)
{
if(x == y) return 1;
if(!y) return 1;
if(y == 1) return x;
else return c(x - 1,y - 1) + c(x - 1,y);
}
void solve()
{
cin >> T;
a[1] = 0,a[2] = 1;
for(long long i = 3;i <= 20 ;i ++) a[i] = (a[i - 1] + a[i - 2]) * (i - 1);
a[1] = 1;
while(T --)
{
long long n,m;
cin >> n >> m;
cout << c(n,n - m) * a[m] << "\n";
}
}
int main()
{
solve();
return 0;
}
汉诺塔
递推公式为 f(n) = 2 ^ n - 1;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL n,k,T;
int main()
{
cin >> T;
while(T --)
{
cin >> n >> k;
cout << ((LL)1 << (n - k))<< endl;
}
}