√复习记录

练习:


1.模拟

洛谷:

P1007 独木桥 https://www.luogu.org/problem/show?pid=1007  http://www.cnblogs.com/gc812/p/6035097.html

第一次第二次成交 https://www.luogu.org/problem/show?pid=2637#sub

//为什么说是搜索!!不就是模拟吗……忍无可忍的丢到模拟的训练分类来了

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 200005
using namespace std;
int n,m;
int a[maxn];
int maxnn,pos;
int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;++i)
    {
        int sum=a[i]*(n-i+1);
        if(sum>maxnn)
        {
            maxnn=sum;
            pos=a[i];
        }
    }
    cout<<pos<<" "<<maxnn;
    puts("");
    return 0;
}
View Code

NOI题库:

18:等差数列末项计算 http://noi.openjudge.cn/ch0103/18/  http://www.cnblogs.com/gc812/p/6035121.html


2.二分

洛谷:

P1258 小车问题  https://www.luogu.org/problem/show?pid=1258  http://www.cnblogs.com/gc812/p/6035174.html

NOI题库:

河口跳石子 http://noi.openjudge.cn/ch0111/10/ 

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define maxn 50005
//#define pi acos(-1.0)
using namespace std;
int n,m,l;
int a[maxn];
bool check(int x)//判断所有的间隔都大于等于该值所需移走的石子数
{
    int last=0,ans=0;
    for(int i=1;i<=n;++i)
    {
        if(a[i]-last<x)++ans;
        else last=a[i];
    }
    if(ans>m)return 0;
    return 1;
}
int main()
{
    scanf("%d%d%d",&l,&n,&m);
    int L=0,r=l,mid;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
    }
    a[++n]=l;
    while(L<=r)
    {
        mid=L+r>>1;
        if(check(mid))L=mid+1;
        else r=mid-1;
    }
    cout<<L-1;
    puts("");
    return 0;
}
//二分查找可能的答案(最大值最小问题和最小值最大跟二分总有一些关系。。。)
View Code

派  http://noi.openjudge.cn/ch0111/05/

注意π的精度

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define maxn 10005
#define pi M_PI
#define eps 1e-5
using namespace std;
int n,f;
double a[maxn];
bool check(double x)//二分所分面积判断可以分成的数量即可
//注意还要留给自己一块。
{
    int sum=0;
    for(int i=1;i<=n;++i)
    {
        sum+=floor(a[i]/x);
    }
    return sum>=f+1;
}
int main()
{
//    freopen("data.txt","r",stdin);
    scanf("%d%d",&n,&f);
    double mid,l=0,r=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%lf",&a[i]);
        a[i]=a[i]*a[i]*pi;
        r=max(r,a[i]);
    }
    while(r-l>eps)//两个浮点数不能判相等 
    {
        mid=(l+r)/2.0;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.3lf",l);
    puts("");
    return 0;
}

月度开销 http://noi.openjudge.cn/ch0111/06/

不难猜想ans在[a[i]max,tot]里 把总和当做r, 然后check, 即连续几个数的和<mid就可以分一组, 分完一组month++, 最后比较month和m即可

 //数组记得开大!开大!开大!

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define maxn 100005
#define pi acos(-1.0)
using namespace std;
int n,m;
int a[maxn],ans=0;
bool check(int x)
{
    int sum=0,month=1;
    for(int i=1;i<=n;++i)
    {
        if(a[i]>x)return 0;
        if(sum+a[i]>x)sum=a[i],month++;
        else sum+=a[i];
    }
    if(month<=m)return 1;
    return 0;
}
int main()
{
//    freopen("data.txt","r",stdin);
    scanf("%d%d",&n,&m);
    int mid,l=0,r=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        r+=a[i];
        l=max(a[i],l);
    }
    while(l<=r)
    {
        mid=l+r>>1;
        if(check(mid))r=mid-1;
        else l=mid+1;
    }
    printf("%d",l);
    puts("");
    return 0;
}
View Code

3.贪心


4.搜索

 vijos:

切蛋糕  https://vijos.org/p/1020

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 2005
using namespace std;
int mid;
int n,m;
int mouth[maxn],cake[maxn];
int tot=0,space;
int sum[maxn],fcake[maxn];

int cmp(const void *a, const void *b)
{
    return(*(int *)a-*(int *)b);
}

bool dfs(int deep,int pos)
{
    if(deep<=0)return 1;
    if(tot-space<sum[mid])return 0;//剪枝
    //dfs时记录当前浪费的蛋糕和
    //总蛋糕量-当前浪费的蛋糕和<当前二分的要满足的嘴大小和
    //就说明无法满足,则退出。
    for(int i=pos;i<=n;++i)
    {
        if(fcake[i]>=mouth[deep])
        {
            fcake[i]-=mouth[deep];
            if(fcake[i]<mouth[1]) space+=fcake[i];
            if(mouth[deep]==mouth[deep-1]) //剪枝
            //dfs时若当前嘴巴大小与下一个相同,则下一个无需从蛋糕1开始枚举
            //直接从当前嘴巴枚举的蛋糕i开始
               {
                  if(dfs(deep-1,i)) return 1;
               }
               else if(dfs(deep-1,1)) return 1;
               if(fcake[i]<mouth[1]) space-=fcake[i];
               fcake[i]+=mouth[deep];
        }
    }
    return 0;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&cake[i]);
        tot+=cake[i];    
    }
    cin>>m;
    for(int i=1;i<=m;++i)
    {
        scanf("%d",&mouth[i]);
    }
     qsort(cake+1,n,sizeof(int),cmp);
    qsort(mouth+1,m,sizeof(int),cmp);
    sum[0]=0;
    for(int i=1;i<=m;++i)sum[i]=sum[i-1]+mouth[i];
    while(sum[m]>tot)--m;//剪枝
    //以最小的嘴累加,与蛋糕总和比较,找到最多能满足的嘴数,就是二分上边界
    int l=0,r=m;
    while(l<=r)
    {
        mid=l+r>>1;    
        for(int i=1;i<=n;++i)fcake[i]=cake[i];
        space=0;
        if(dfs(mid,1))l=mid+1;
        else r=mid-1;        
    }
    cout<<r;
    puts("");
    return 0;
}
View Code

 扫雷游戏  https://www.luogu.org/problem/show?pid=2670

 //虽然洛谷给的标签是搜索但是好想把它扔到模拟去

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 205
using namespace std;
int n,m;
char a[maxn][maxn];
int lei[maxn][maxn];
int main()
{
    cin>>n>>m;
    memset(lei,0,sizeof(lei));
    for(int i=0;i<n;++i)scanf("%s",a[i]);
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<n;++j)
        {
//            cin>>a[i][j];
            if(a[i][j]=='*')
            {
                ++lei[i-1][j+1];
                ++lei[i-1][j];
                ++lei[i-1][j-1];
                ++lei[i][j-1];
                ++lei[i][j+1];
                ++lei[i+1][j];
                ++lei[i+1][j-1];
                ++lei[i+1][j+1];
            }
        }
    }
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<m;++j)
        {
            if(a[i][j]=='*')printf("*");
            else printf("%d",lei[i][j]);
        }
        puts("");
    }
    return 0;
}
View Code

 迷宫  https://www.luogu.org/problem/show?pid=1605

终点有可能有障碍物,这时必须输出0,

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 200005
using namespace std;
int n,m,t;
int sx,sy,fx,fy;
bool a[10][10];
int ans=0;
bool check(int x,int y)
{
    if(x==fx&&y==fy)
    {
        ++ans;
        return 1;
    }
    if(a[x][y])return 1;
    return 0;
}
void dfs(int x,int y)
{
    a[x][y]=1;
    if(x>1&&!check(x-1,y))dfs(x-1,y);
    if(x<n&&!check(x+1,y))dfs(x+1,y);
    if(y>1&&!check(x,y-1))dfs(x,y-1);
    if(y<n&&!check(x,y+1))dfs(x,y+1);
    a[x][y]=0;
}
int main()
{
    cin>>n>>m>>t>>sx>>sy>>fx>>fy;
    int x,y;
    for(int i=1;i<=t;++i)
    {
        scanf("%d%d",&x,&y);
        if(x==fx&&y==fy)
        {
            puts("0");
            return 0;
        }
        a[x][y]=1;
    }
    dfs(sx,sy);
    cout<<ans;
    puts("");
    return 0;
}
View Code

*5.图论

寻找道路  http://www.cnblogs.com/gc812/p/6024717.html

营救    https://www.luogu.org/problem/show?pid=1396

通过spfa选边 大于mid的都不选 最后用spfa判断从起点s出发能否到达终点t 二分答案

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#define maxn 400005
using namespace std;
int n,m,s,t;
int dis[maxn],cnt=0,h[maxn];
bool vis[maxn];

struct data
{
    int next,w,to;
}e[maxn];

queue<int>q;

void ins(int a,int b,int c)
{
    e[++cnt].to=b;
    e[cnt].next=h[a];
    h[a]=cnt;
    e[cnt].w=c;
}

bool check(int d)
{
    memset(vis,0,sizeof(vis));
    memset(dis,127/3,sizeof(dis));
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        vis[x]=0;
        for(int i=h[x];i;i=e[i].next)
        {                
            if(dis[e[i].to]>e[i].w)
            {
                dis[e[i].to]=e[i].w;
                if(!vis[e[i].to]&&dis[e[i].to]<=d)
                {
                    vis[e[i].to]=1;
                    q.push(e[i].to);
                }
            }                        
        }
    }
    return dis[t]<=d;
}
int main()
{
//    freopen("data.txt","r",stdin);
//    cout<<"aaa"<<endl;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    int a,b,c;
    int l=maxn,r=0,mid;
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        ins(a,b,c);ins(b,a,c);
        l=min(l,c);
        r=max(r,c);
    }
    while(l<r)
    {
        mid=l+r>>1;
        if(check(mid))r=mid;
        else l=mid+1;
    }
    printf("%d",l);
    puts("");
    return 0;
}
View Code

2016.11.14

//不知道为什么最近脑洞比较大写题莫名其妙的循环起了痒?。

//提交的时候真的不要手贱保留了freopen……不要问我怎么知道的

//已经手贱因为freopen一早上爆零7次了还有救吗 急 在线等TUT

2016.11.15

//二分上下界还很模糊 mid的判断全靠rp和样例调试+1-1 好蠢哦

//发现搜索已经忘光了???

原文地址:https://www.cnblogs.com/gc812/p/6034907.html