標籤: 數學php
hdu_5795 sg函數應用java
題意:c++
兩我的從不少堆中取石子,每次能夠從其中任意一堆中取一個或者多個,也能夠吧其中任意一堆分紅三堆,問先手贏仍是後手贏。數組
題解:ide
先講解一下sg函數的意義:sg函數表示的是當前這一堆石子的全部可能的後繼狀態中第一個沒有出現過的狀態值,若是在這個題中,若是隻考慮拿取石子,不考慮分堆狀況的話,能夠看到,對於一堆石子個數是x的一堆石子來講,它的sg函數就是x.
全部堆的sg函數異或起來就是整個堆的sg函數,若是等於0是一種狀態,不等於0是另一種狀態,根據題意判讀便可,可是這個題有分堆的狀況,考慮一下要怎麼作呢,採用打表找規律的方式,由於枚舉每一個堆分堆後的後繼狀態,而後找每一堆對應的sg函數值得規律,咱們能夠發現當x = 8k+7和x = 8k+8的時候sg分別等於 sg = 8k+8和sg = 8k + 7以外,其餘的sg = x;
那麼總結一下這種題的思路,對於每一堆枚舉它的sg函數值,找到對應每一堆的sg函數值,而後異或起來,獲得全部堆的sg函數,而後根據其是否得0來判讀狀態。函數
代碼spa
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); int ans = 0; int tm; int sg; for(int i= 0; i < n ;i++){ scanf("%d",&tm); if(tm%8==7) sg = tm+1; else if(tm%8==0) sg = tm-1; else sg = tm; ans = ans^sg; } if(ans==0) puts("Second player wins."); else puts("First player wins."); } return 0; }
hdu_1024 dp經典code
題意:ip
求一個數組的m個不重合子區間的和最大值get
題解:
經典的dp,dp[i][j]表示組成了i個集合,用到了前j個元素的時候的子區間最大值。那麼有轉移方程爲,考慮當前這個元素加入到前面這個集合,或者這個元素不加入前面這個集合,而是做爲一個新的集合的起點
\(dp[i][j] = max(dp[i][j-1]+a[j],max(dp[i-1][k]+a[k])\)
注意,這裏很明顯,要遍歷每次\(0<k<j\)因此若是每次都掃一遍的話確定會超時,咱們能夠每次算上一層的時候保存一個上一層到k的最大值,由於空間問題,要使用滾動數組,和其餘的dp同樣,畫一個狀態轉移的圖,而後找規律便可。
代碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int RINF = -1000000009; const int N = 1000008; int dp[N]; int mp[N]; int mmax[N]; int main() { int n,m; int tm; while(~scanf("%d%d",&m,&n)) { for(int i = 1; i <= n; i++){ scanf("%d",&mp[i]); } memset(dp,0,sizeof(dp)); memset(mmax,0,sizeof(mmax)); mmax[0] = 0; for(int i = 1; i <= m; i++){ tm = RINF; for(int j = i; j <= n; j++){ dp[j] = max(dp[j-1]+mp[j],mmax[j-1]+mp[j]); mmax[j-1] = tm; tm = max(tm,dp[j]); } } printf("%d\n",tm); } return 0; }
hdu_5762 暴力+鴿巢原理
題意:
給你不少點,求存不存在兩組不一樣的點對知足其哈密頓距離相同
題解:
考慮到兩點的距離的全部可能解爲2M,因此若是點數對大於2M的話,確定存在兩個點數對的距離和是相同的的。因此直接暴力可能能夠在規定時間內結束循環。那麼咱們就直接暴力好了,哈哈
代碼:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int N = 300004; int xx[N],yy[N]; int vis[N]; int aabs(int a) { if(a>=0) return a; else return -a; } int main() { int T,n,m; scanf("%d",&T); while(T--) { memset(xx,0,sizeof(xx)); memset(yy,0,sizeof(yy)); memset(vis,0,sizeof(vis)); scanf("%d%d",&n,&m); int x,y; for(int i = 0; i < n; i++){ scanf("%d%d",&xx[i],&yy[i]); } bool fl = 0; for(int i = 0; i <n&&fl==0; i++){ for(int j = i+1; j < n&&fl==0; j++){ int tm = aabs(xx[i]-xx[j])+aabs(yy[i]-yy[j]); if(vis[tm]) { fl = 1; } else vis[tm] = 1; } } if(fl) puts("YES"); else puts("NO"); } return 0; }
hdu_1808 鴿巢原理+前綴和
題意:
求一個數組,存不存在一個子序列的和是c的倍數
題解:
考慮到這個題已知n>c因此對於n個數的前綴和%c必定存在兩個相同的值,那麼求前n個數的前綴和%c那麼若是兩個數相同了,那麼在這兩個數之間的數加起來確定會和是c的倍數,能夠本身驗證
代碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 100006; int sum[N]; int vis[N]; int main() { int n,m,tm; int l,r; while(~scanf("%d%d",&n,&m)) { memset(sum,0,sizeof(sum)); memset(vis,0,sizeof(vis)); if(n==0&&m==0) return 0; bool fl = 0; for(int i = 1; i <= m; i++){ scanf("%d",&tm); if(fl==1) continue; sum[i] = (sum[i-1]+tm)%n; if(tm%n==0){ fl = 1; l = r = i; } else if(sum[i]%n==0) { fl = 1; l = 0; r = i; } else if(vis[sum[i]]!=0){ fl = 1; l = vis[sum[i]]; r = i; } else { vis[sum[i]] = i; } } for(int i = l+1; i <= r; i++){ printf("%d",i); if(i!=r) printf(" "); } puts(""); } return 0; }
hdu_1205 組合數學
題意:
略
題解:
用隔板法考慮,考慮個數最多的糖果若是能夠經過隔板法分開,那麼其餘的糖果就必定能夠分割開了,爲啥本身想
代碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 1000008; #define ll long long ll mp[N]; int main() { int T,n; scanf("%d",&T); while(T--) { ll mmax = 0; ll sum = 0; scanf("%d",&n); for(int i = 0; i < n; i++){ scanf("%lld",&mp[i]); mmax = max(mmax,mp[i]); sum+=mp[i]; } if(sum-mmax+1>=mmax) puts("Yes"); else puts("No"); } return 0; }
hdu_2048 組合數學
題意:
之後中文題面的都不介紹題意了
題解:
經典的錯排問題。考慮N我的,若是將其中一我的y放在另外一個位置x上,那麼x這我的就有兩種可能,一種是x就去y的位置上,那麼剩下的人就有\(f(n-2)\)種可能,另外一種狀況就是x沒有去y的位置,那麼就是剩下的n-1我的錯排,\(f(n-1)\)
因而有了這樣的公式
\(f(n) = (n-1)*[f(n-2)+f(n-1)]\)
固然,也能夠套用錯排的公式
\[ f(n) = n!(1-\frac{1}{1!}+\frac{1}{2!}-...\pm \frac{1}{n!}) \]
代碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; double ans[22]; double dp[22]; void init() { for(int i = 0; i < 22; i++){ dp[i] = 0; ans[i] = 0; } ans[1] = 0.0; ans[2] = 50.0; dp[1] = 0.0; dp[2] = 1.0; double tm; for(int i = 3; i < 22; i++){ tm = 1; for(int j = 1; j <= i; j++){ tm = tm*j; } dp[i] = ((double)i-1)*(dp[i-1]+dp[i-2]); // printf("dp = %lf ",dp[i]); ans[i] = dp[i]/tm*100; } } int main() { init(); int T,c; scanf("%d",&T); while(T--) { scanf("%d",&c); printf("%.2lf%%\n",ans[c]); } return 0; }
poj_2356 鴿巢原理
- 題意:
求一個區間是否存在幾個數的的和是n的整數倍
- 題解:
根據鴿巢原理,\(x\%n\) 最多有n-1種狀況,那麼若是有n個數的話,因此求完前綴和後對n取模,而後若是結果又兩個相同的話,那麼必定這兩個數之間的數的和是n的倍數,爲何的話,本身推
- 代碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 10008; int sum[N]; int vis[N]; int mp[N]; int main() { int n; int l, r; int tm; while(~scanf("%d",&n)) { memset(sum,0,sizeof(sum)); memset(vis,0,sizeof(vis)); for(int i = 1; i <= n; i++) { scanf("%d",&tm); mp[i] = tm; sum[i] = (sum[i-1]+tm)%n; if(sum[i]%n==0){ l = 0; r = i; } else if(vis[sum[i]%n]){ l = vis[sum[i]%n]; r = i; } else { vis[sum[i]%n] = i; } } printf("%d\n",r-l); for(int i = l+1; i <= r; i++){ printf("%d\n",mp[i]); } } return 0; }
csu_1320 卡特蘭數
- 題解:
和括號的匹配差很少的想法,裸的卡特蘭數,直接套用公式下面給出卡特蘭數的三個公式
- 代碼:
#include <cstdio> typedef long long ll; const int MAXN = 10000 + 100; const ll MOD = 1000000007; ll ans[MAXN]; ll extgcd(ll a, ll b, ll &x, ll &y){ ll d = a; if(b == 0LL){ x = 1; y = 0; }else{ d = extgcd(b, a % b, y, x); y -= (a / b) * x; } return d; } ll mod_inverse(ll a, ll MOD){ ll x, y; extgcd(a, MOD, x ,y); return (x % MOD + MOD) % MOD; } void Init(){ ans[0] = ans[1] = 1; for(int i = 2; i < MAXN; ++i){ ans[i] = (((ans[i - 1] * (4 * i - 2)) % MOD) * mod_inverse(i + 1, MOD)) % MOD; } return; } int main(int argc, char const *argv[]){ Init(); int x; while(~scanf("%d", &x)){ printf("%lld\n", ans[x]); } return 0; }
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MOD = 1000000007; const int N = 10008; #define ll long long ll f[N]; int n; void getf() { f[0] = 1; for(int i = 0; i < N; i++){ for(int j = 0; j < i; j++){ f[i] = ((f[j]*f[i-1-j])%MOD+f[i])%MOD; } } } int main() { getf(); while(~scanf("%d",&n)) { printf("%lld\n",f[n]); } return 0; }
uva_10790 組合數學
- 題意:
有兩排,上面給出必定的點數,下面也給出必定的點數,而後問你,交點的個數,多條線不能相交於同一個點
- 題解:
上面選擇兩個點,下面選擇兩個點必定就有一個交點,因此咱們直接\[\dbinom{a}{2}*\dbinom{b}{2}\]
- 代碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 20008; #define ll long long int main() { ll a,b; ll ans; int cnt = 1; while(~scanf("%lld%lld",&a,&b)) { if(a==0&&b==0) return 0; ans = a*b*(a-1)*(b-1)/4; printf("Case %d: %lld\n",cnt++,ans); } return 0; }
hdu_1023 卡特蘭數+java大數
- 題意:
求火車進站和出棧的順序的種類數,經典的卡特蘭數,可是100的卡特蘭數要用大數算。
- 代碼:
import java.math.BigInteger; import java.util.Scanner; public class Main { public static BigInteger f[] = new BigInteger[101]; public static void main(String[] args) { Scanner scan = new Scanner(System.in); f[0] = BigInteger.ONE; for(int i=1;i<101;i++) { BigInteger a = BigInteger.valueOf(4*i-2); BigInteger b = BigInteger.valueOf(i+1); f[i] = a.multiply(f[i-1]).divide(b); } while(scan.hasNext()){ int n = scan.nextInt(); System.out.println(f[n]); } } }