2/17qbxt筆記(挺全了)

緩存機制

在電腦中有一種比內存讀入還要快的東西叫作緩存,其定義是對於從內存裏讀入一個數組時,計算機會預測剩餘的連續的部分數組是否會被用到,因此將他存進緩存,這樣能夠直接按順序讀入,不用查詢,更加快。node

那麼對於咱們的二維數組 \(f[i][j]\) 他是以長度爲 \(1\times j\)\(i\) 個線段組成一個線性的長鏈,因此連續的數字之間是 \(f[1][1],f[1][2]\), 而並非 \(f[1][1],f[2][1]\),ios

那麼推廣到咱們的 \(for\) 循環上,若咱們按照外層 \(i\),內層 \(j\) 的順序循環,獲得的數組就是按照計算機存儲順序調用,所以能夠直接從緩存裏讀入 ,可是當咱們反過來,計算機沒法預測咱們的順序,即緩存中的數組是無用的(當前),那麼只能調用內存,時間的差距大約在 \(10\)左右c++

因此說對於循環的枚舉順序關係到時間複雜度是很重要的.尤爲是矩陣乘除,不一樣的運輸順序形成的極值可達 \(4\) 倍,這就是後幾個點被卡的緣由,特別虧。數組

二分

解決問題,有 \(m\) 詢問,數列已經排好序,求出小於 \(x\) 的有多少個緩存

思路

知道最大的 \(a_i<= x\) ,那麼就能夠說答案就是 \(i\)函數

int n,m,a[B];

int main() {
  n=read(),m=read();
  for (int i=1;i<=n;i++) a[i]=read();
  sort(a+1,a+1+n); 
  while(m--)
  {
    int x=read();
    int l=1, r=n, ans=0;
    while(l<=r)
    {
      int mid=(l+r)>>1;
      if(a[mid]<=x) l=mid+1, ans=mid;
      else r=mid-1;
    }
    printf("%d\n",ans);
  }
  return 0;
}

二分答案

給定 \(N\) 根繩子,要求你從中分出 \(K\) 段繩子,使得每段繩子長度同樣而且必須是整數,問這 \(K\) 段繩子最長可以是多少。優化

思路

這個題應該好好地說一下,浪費了我一下午,讓個人對doubleint有了從新的認識spa

作法的話很簡單,二分長度 看行成的段數是否大於 \(K\),二分答案就行了code

可是這個題須要用的精度,由於在二分的過程當中獲得的數始終是小數,可是咱們定義的是整形,因此會無限死循環blog

再就是在最後的強制類型轉換中,我發現了三種不用的輸出方式,獲得了三種不一樣的分數,過程慘痛

cout<<a;

這樣直接是雙精度輸出,沒什麼變化

printf("%.f",a)

這個可神了,竟然會自動四捨五入,太神奇了,我才知道,答案部分正確

int s=(int)a;
cout<<s;

強制類型轉換,這個就能夠將整數部分完整的獲得了,這也是最後的正確答案,

double s[B],sum;
const double esp=1e-8;
int n,k;

 main() {
//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);
  cin>>n>>k;
  double mx=0;
  for (int i=1;i<=n;i++)
  {
    cin>>s[i];
    mx=max(mx,s[i]);
  }
  double l=0,r=mx;
  while(l+esp<r) 
  {
    double mid=(l+r)/2;
    int num=0;
    for (int i=1;i<=n;i++)  num+=s[i]/mid;
    if(num>=k) l=mid;
    else r=mid;
  }
  int s=(int)l;
  cout<<s;
  return 0;
}

貪心

國王遊戲

怎麼對大臣排序???

絕對按照右手排,貌似不對,md

假設 \(n==2\) 時候的到一種 \(cmp\) 的函數,而後直接排序就能夠了,這就是貪心,信息學是一門不須要證實的許可,不用證實,對於\(\%90\) 的貪心題均可這麼作,\(nb\)

思路

咱們不妨設 \(n=2\) 時,最大獲益金幣的人最小

那第一我的 \(A\)\(a.r,a.l\), 第二我的 \(B\)\(b.r,b.l\)

設皇上的左右手分別爲 \(z[0].l, z[0].r\)

\(A\)\(B\) 的前邊時

\(A\) 的錢

\[\frac{z[0].l}{a.r} \]

\(B\) 的錢

\[\frac{z[0].l\times a.l}{b.r} \]

答案 \(v1\)\(max\{\frac{z[0].l}{a.r},\frac{z[0].l\times a.l}{b.r}\}\)

\(B\)\(A\) 前面時

同理可得答案 \(v2\)\(max\{\frac{z[0].l}{b.r},\frac{z[0].l\times b.l}{a.r}\}\)

最終答案就是 \(max\{v1,v2\}\)

那麼當 \(n!=2\) 時呢,假設如今就這兩我的須要咱們討論,咱們不是須要國王到本身前面人左手的數嘛,咱們設這個數爲 \(x\) 那麼式子的就是變成

\[v1=max\{\frac{x}{a.r},\frac{x\times a.l}{b.r}\}\\ v2=max\{\frac{x}{b.r},\frac{x\times b.l}{a.r}\}\\ \]

答案就是 \(ans=max\{v1,v2\}\)

當咱們把分母去掉即,\(v1,v2\) 都乘以\(a.r\times b.r\)

化簡得

\[v1=max\{b.r\times x,x\times a.l\times a.r\}\\ v2=max\{a.r\times x,x\times b.l\times b.r\}\\ \]

咱們發現 \(x\) 能夠消掉,即在這裏答案的大小不受 \(x\) 的影響,那麼咱們的式子就能夠變成

\[v1=max\{b.r,a.l\times a.r\}\\ v2=max\{a.r, b.l\times b.r\}\\ \]

有了式子咱們就能夠利用冒泡排序,時間複雜度爲 \(O(n^2)\)

//冒泡排序
#include <iostream>

using namespace std;

struct node{
	int l,r;
};
node z[100010];

int cmo(node a, node b)//a是否放在b的前面
{
	int v1 = max(z[0].l/a.r, z[0].l*a.l / b.r);
	int v2 = max(z[0].l/b.r, z[0].l*b.l / a.r);
	
	if(v1<v2) return 1;
	else return 0;
}
int main()
{
	for (int i=1;i<=n;i++)
		for (int j=1;j<n;j++)
		{
			if(cmp(z[j+1],z[j])) swap(z[j+1], z[j]);
		}
}// O(n^2)

其實就變成了比較這四個數的大小,這樣就能夠進行貪心了

貪心式子是否能夠優化呢

咱們不難發現 \(b.r<b.l\times b.r\)\(a.r<a.l\times a.r\)

那麼咱們比較這是兩個還有意義嘛,他們兩個本質上就比右側的大,因此剔除就行了,所以式子就化簡成

\[ans=max\{b.r\times b.l, a.r\times a.l\} \]

貪心化簡的本質

  1. 去分母
  2. 去除公共項
  3. 出去無用項,即不可能更好
  4. 簡化完畢!
struct node{
  int l, r;
};

node z[B];
int ans=1, n;

int cmp(node a, node b)
{
  return a.l*a.r<b.l*b.r;
}

int main() {
  scanf("%d",&n);
  scanf("%d%d",&z[0].l,&z[0].r); 
  for (int i=1;i<=n;i++)
    scanf("%d%d",&z[i].l,&z[i].r);
    
  sort(z+1,z+1+n,cmp);
  for (int i=0;i<n;i++) ans*=z[i].l;
  printf("%d",ans/z[n].r);  
  return 0;
}

高精作法

#include <bits/stdc++.h>
using namespace std;
int now[20010],sum[20010],ans[20010],add[20010];
struct Node {
    int a;
    int b;
    long long a_b;
}node[1010];
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
void times(int x) {
    memset(add,0,sizeof(add));
    for(int i=1;i<=ans[0];i++) {
        ans[i]=ans[i]*x;
        add[i+1]+=ans[i]/10;
        ans[i]%=10;
    }
    for(int i=1;i<=ans[0]+4;i++) {
        ans[i]+=add[i];
        if(ans[i]>=10) {
            ans[i+1]+=ans[i]/10;
            ans[i]%=10;
        }
        if(ans[i]!=0) {
            ans[0]=max(ans[0],i);
        } 
    }
    return ;
}
int divition(int x) {
    memset(add,0,sizeof(add));
    int q=0;
    for(int i=ans[0];i>=1;i--) {
        q*=10;
        q+=ans[i];
        add[i]=q/x;
        if(add[0]==0 && add[i]!=0) {
            add[0]=i;
        }
        q%=x; 
    }
    return 0;
}
bool compare() {
    if(sum[0]==add[0]) {
        for(int i=add[0];i>=1;i--) {
            if(add[i]>sum[i]) return 1;
            if(add[i]<sum[i]) return 0;
        }
    }
    if(add[0]>sum[0]) return 1;
    if(add[0]<sum[0]) return 0;
}
void cp () {
    memset(sum,0,sizeof(sum));
    for(int i=add[0];i>=0;i--) {
        sum[i]=add[i];
    }
    return ;
}
bool cmp(Node a,Node b) {
    return a.a_b<b.a_b;
}
int main() {
    int n=read();
    for(int i=0;i<=n;i++) {
        node[i].a=read(),node[i].b=read();
        node[i].a_b=node[i].a*node[i].b;
    }
    sort(node+1,node+n+1,cmp);
    ans[0]=1,ans[1]=1;
    for(int i=1;i<=n;i++) {
        times(node[i-1].a);
        divition(node[i].b);
        if(compare()) {
            cp();
        }
    }
    for(int i=sum[0];i>=1;i--)
        printf("%d",sum[i]);
    return 0;
}

輸入輸出優化

快出

char s[10000000];
int l=0;

void print(int x)
{
	int y=0,z=0;//倒寫
	while (x!=0)
	{
		y=y*10+x%10;
		z++;
		x/=10;
	}
	for (int a=1;a<=z;a++)
	{
		s[l++]=y%10+'0';
		y/=10;
	}
	s[l++]='\n';
}

空間的計算方法

  • \(char\) 1 字節
  • \(int\) 4字節
  • \(long\ long\) 8 字節
  • \(1M=1024K\)
  • \(10^7=10M\)

搜索

  • BFS
  • DFS

題型: 給 \(N\) 個數選 \(k\) 個數

求和最小---最優解問題

求方案數---解數量問題

給出一種符合條件的方案---可行解問題

如何判斷使用哪一種搜索手段

BFS

  1. 必須最優解問題,從當前狀態到另一個狀態代價變化 必須爲1

DFS

  1. ,剩下的都是DFS

搜索優化

可行性剪枝

明顯不可能的解直接扔掉

最優性剪枝

不可能成爲最有解的剪枝

void dfs(int p, int nowsum, int nowuse)//考慮地p,和爲nowsum,選了nowuse個數
{
    if (nowuse>k) return;//可行性剪枝
    if (nowuse + n-p+1 < k) return;//可行性剪枝
    if (nowsum>=ans) return;//最優性剪枝
	if(p>n)
	{
		ans=min(nowsum,k);
		return;
	}
	dfs(p+1,nowsum,nowuse);
	dfs(p+1,nowsum+a[p];nowsue+1);
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	dfs(1,0,0);
	printf("%d",ans);
	return 0;
}

搜索順序優化

若是排序後再接搜索的話,搜索順序會受到影響,即先搜索可行性優解

能夠選擇可使答案更好或者更快地到答案的題

靶心數

void dfs(int p, int nowsum, int nowuse)//考慮地p,和爲nowsum,選了nowuse個數
{
    if (nowuse>k) return;//可行性剪枝
    if (nowuse + n-p+1 < k) return;//可行性剪枝
    if (nowsum>=ans) return;//最優性剪枝
	if(p>n)
	{
		ans=min(nowsum,k);
		return;
	}
    dfs(p+1,nowsum+a[p];nowsue+1);
	dfs(p+1,nowsum,nowuse);

}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
    sort(a+1,a+1+n);
	dfs(1,0,0);
	printf("%d",ans);
	return 0;
}
相關文章
相關標籤/搜索