算法竞赛入门经典 写题笔记(第一章 算法设计基础)

可是我为什么要专门开一发写这个呢......

1.1 思维的体操

例题1 The Dragon of Loowater

贪心

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
int x[20005],y[20005];
int main()
{

    while(~scanf("%d %d",&n,&m)&&(n||m))
    {
        for(int i=0;i<n;i++) scanf("%d",&x[i]);
        for(int i=0;i<m;i++) scanf("%d",&y[i]);
        sort(x,x+n);
        sort(y,y+m);
        int k=0;
        int num=0;
        for(int i=0;i<m;i++)
        {
            if(x[k]<=y[i])
            {
                num+=y[i];
                k++;
                if(k==n) break;
            }
        }
        if(k<n) printf("Loowater is doomed!\n");
        else printf("%d\n",num);
    }
    return 0;
}

例题2 Commando War

贪心

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,cnt;
struct Node{int a,b;}node[MAXN];
inline bool cmp(struct Node x,struct Node y){return x.b>y.b;}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        for(int i=1;i<=n;i++)
            scanf("%d%d",&node[i].a,&node[i].b);
        sort(&node[1],&node[n+1],cmp);
        long long ans=0,sum=0;
        for(int i=1;i<=n;i++)
        {
            sum+=node[i].a;
            ans=max(ans,sum+node[i].b);
        }
        cnt++;
        printf("Case %d: %lld\n",cnt,ans);
    }
    return 0;
}

例题3 Spreading the Wealth(中位数,贪心)

题目模型:给定数轴上的n个点,在数轴上的所有点中,中位数离所有的顶点距离之和最小。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1000010
using namespace std;
int n;
long long a[MAXN],c[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        long long tot=0;
        for(int i=1;i<=n;i++) tot+=a[i];
        long long M=tot/n;
        c[0]=0;
        for(int i=1;i<n;i++) c[i]=c[i-1]+a[i]-M;
        sort(&c[0],&c[n]);
        long long cur=c[n/2];
        long long ans=0;
        for(int i=0;i<n;i++) ans+=abs(cur-c[i]);
        printf("%lld\n",ans);
    }
    return 0;
}

例题4 Graveyard

坐标的放缩法qwq

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1000010
using namespace std;
int n,m;
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d%d",&n,&m)==2)
    {
        double ans=0.0;
        for(int i=1;i<=n;i++)
        {
            double pos=(double)(i*1.0/n)*(n+m);
            ans+=fabs(pos-1.0*floor(pos+0.5))/(n+m);
        }
        printf("%.4lf\n",ans*10000);
    }
    return 0;
}

例题5 Piotr's Ants

比较神奇的是要看出来这道题里面隐藏的性质:
1、蚂蚁掉头的时候相当于对穿而过。比如说一个位置为1,方向为右的,两秒之后一定会有一个蚂蚁在位置为3上,方向为右,但是不一定是这只蚂蚁。
2、因为碰撞会掉头,所以蚂蚁开始和结束的相对位置不变。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define MAXN 100010
using namespace std;
int T,l,t,n,cnt;
int Pos[MAXN];
char s[10];
struct Node{int pos,op,id;}node[MAXN],en[MAXN];
inline bool cmp(struct Node x,struct Node y){return x.pos<y.pos;}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&l,&t,&n);
        cnt++;
        for(int i=1;i<=n;i++) 
        {
            scanf("%d",&node[i].pos);
            scanf("%s",s);
            if(s[0]=='R') node[i].op=1;
            else node[i].op=-1;
            node[i].id=i;
            en[i].pos=node[i].pos+node[i].op*t;
            en[i].id=i;
            en[i].op=node[i].op;
        }
//      for(int i=1;i<=n;i++) printf("i=%d %d %d\n",i,node[i].pos,node[i].op);
        sort(&node[1],&node[n+1],cmp);
        for(int i=1;i<=n;i++) Pos[node[i].id]=i;
        sort(&en[1],&en[n+1],cmp);
        for(int i=1;i<n;i++)
        {
            if(en[i].pos==en[i+1].pos) en[i].op=en[i+1].op=0;
        }
        printf("Case #%d:\n",cnt);
        for(int i=1;i<=n;i++)
        {
            int cur=Pos[i];
            if(en[cur].pos<0||en[cur].pos>l) printf("Fell off\n");
            else 
            {
                printf("%d ",en[cur].pos);
                if(en[cur].op==-1) printf("L\n");
                else if(en[cur].op==0) printf("Turning\n");
                else printf("R\n");
            }
        }
        printf("\n");
    }
    return 0;
}

例题6 Image Is Everything

例题7 Even Parity

枚举状态的化简
虽然我们不能枚举所有可能的情况,但是我们可以找到依赖关系,通过已知条件确定未知的方法来减少枚举的数量。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 50
using namespace std;
int T,n,cnt;
int a[MAXN][MAXN],cur[MAXN][MAXN];
inline int solve(int x,int y)
{
    int sum=0;
    if(x-2>=1) sum+=cur[x-2][y];
    if(y-1>=1) sum+=cur[x-1][y-1];
    if(y+1<=n) sum+=cur[x-1][y+1];
    return sum;
}
inline void print()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            printf("%d ",cur[i][j]|a[i][j]);
        printf("\n");
    }
    printf("\n");
}
inline int calc(int x)
{
    for(int i=0;i<n;i++)
    {
        if(x&(1<<i)) cur[1][i+1]=1;
        else if(a[1][i+1]==1) return 0x3f3f3f3f;
    }
    for(int i=2;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(solve(i,j)&1) cur[i][j]=1;
            else 
            {
                if(a[i][j]==1) return 0x3f3f3f3f;
                else cur[i][j]=0;
            }
        }
    int cur_ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(a[i][j]==0&&cur[i][j]==1)
                cur_ans++;
    // print();
    // printf("cur_ans=%d\n",cur_ans);
    return cur_ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        cnt++;
        int jian=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
        int cur_ans=0x3f3f3f3f;
        for(int i=0;i<(1<<n);i++) memset(cur,0,sizeof(cur)),cur_ans=min(cur_ans,calc(i));
        if(cur_ans==0x3f3f3f3f) printf("Case %d: %d\n",cnt,-1);
        else printf("Case %d: %d\n",cnt,cur_ans);
    }
    return 0;
}

例题8 Colored Cubes

例题9 Chinese Mahjong

例题10 Help is needed for Dexter

给定正整数n,你的任务是用最少的操作次数把序列1,2,3...,n中的所有数都变成0,每次操作可以从序列中选择一个或者多个证书,同时减去一个相同的正整数。
f(n)=f(n/2)+1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int n;
inline int solve(int x)
{
    if(x==1) return 1;
    else return solve(x/2)+1;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)!=EOF)
        printf("%d\n",solve(n));
    return 0;
}

例题11 A Different Task

例题12 Assemble

二分+贪心

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<ctime>
#include<vector>
#define MAXN 1010
using namespace std;
int T,n,m,cnt,ans;
struct Node{string fa,name;int price,value;}node[MAXN<<1];
struct Node2{int price,value;};
map<string,int>id;
vector<Node2>vec[MAXN];
inline bool check(int x)
{
    int cur_ans=0;
    for(int i=1;i<=cnt;i++)
    {
        int minn=2147483647;
        for(int j=0;j<vec[i].size();j++)
        {
            if(vec[i][j].value<x) continue;
            minn=min(minn,vec[i][j].price);
        }
        if(minn==2147483647) return false;
        cur_ans+=minn;
        if(cur_ans>m) return false;
        
    }
    return true;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        cnt=0;
        id.clear();
        for(int i=1;i<=n;i++) vec[i].clear();
        for(int i=1;i<=n;i++)
        {
            cin>>node[i].fa>>node[i].name;
            scanf("%d%d",&node[i].price,&node[i].value);
            if(!id.count(node[i].fa)) id[node[i].fa]=++cnt;
            vec[id[node[i].fa]].push_back((Node2){node[i].price,node[i].value});
        }
        int l=0,r=(int)1e9+1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)==true) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

例题13 Pie

虽然不是“最小值最大”诸如此类的问题,但是采取二分答案的方法可以使得问题转化为判定性问题
double类型的二分,注意eps比要求的精度多个2位就可以了,太多的话,二分会T

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define eps 1e-5
#define pi acos(-1.0)
#define MAXN 100010
using namespace std;
int n,f,T;
double l=0.0,r;
double R[MAXN];
inline bool check(double x)
{
    int cur_ans=0;
    for(int i=1;i<=n;i++)
    {
        double s=pi*R[i]*R[i];
        cur_ans+=floor(s/x);
    }
    if(cur_ans>=f) return true;
    else return false;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&f);
        l=r=0.0;
        for(int i=1;i<=n;i++)
        {
            int cur;
            scanf("%d",&cur);
            R[i]=1.0*cur;
            r=max(r,R[i]*R[i]*pi);
        }
        f++;
        while(l+eps<r)
        {
            double mid=(l+r)/2;
            if(check(mid)==true) l=mid;
            else r=mid;
        }
        printf("%.4lf\n",l);
    }
    return 0;
}

例题14 Fill the Square

贪心

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define MAXN 110
using namespace std;
int T,n,m,cnt;
char a[MAXN][MAXN],s[MAXN];
inline void solve(int x,int y)
{
    for(char ch='A';ch<='Z';ch++)
    {
        bool flag=true;
        if(x>1&&a[x-1][y]==ch) flag=false;
        if(x<n&&a[x+1][y]==ch) flag=false;
        if(y>1&&a[x][y-1]==ch) flag=false;
        if(y<n&&a[x][y+1]==ch) flag=false;
        if(flag==true){a[x][y]=ch;break;}
    }
}
int main()
{

    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        cnt++;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                cin>>a[i][j];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(a[i][j]=='.')
                    solve(i,j);
        printf("Case %d:\n",cnt);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                printf("%c",a[i][j]);
            printf("\n");
        }
    }
    return 0;
}

例题15 Network

在一个无根树上设定最少的关键点,使得树上每个点到关键点的距离都不超过K。
无根树转有根树,然后从叶子节点开始,每次贪心地设定当前深度最大的叶子节点的K级祖先为关键节点。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define MAXN 100010
using namespace std;
int T,n,m,s,t,ans,k;
int head[MAXN<<1],done[MAXN];
vector<int>d[MAXN];
struct Edge{int nxt,to;}edge[MAXN<<1];
struct Node{int fa,dep;}node[MAXN];
inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;}
inline void dfs1(int x,int pre)
{
    node[x].fa=pre;
    node[x].dep=node[pre].dep+1;
    bool flag=false;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==pre) continue;
        flag=true;
        dfs1(v,x);
    }
    if(flag==false) d[node[x].dep].push_back(x);
}
inline void dfs2(int x,int pre,int cnt)
{
    if(cnt>k) return;
    done[x]=1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==pre) continue;
        dfs2(v,x,cnt+1);
    }
}
inline void solve()
{
    for(int i=n;i>k+1;i--)
    {
        if(d[i].size()==0) continue;
        for(int j=0;j<d[i].size();j++)
        {
            int x=d[i][j];
            if(done[x]) continue;
            int v=x;
            for(int tot=1;tot<=k;tot++) v=node[v].fa;
//          printf("x=%d v=%d\n",x,v);
            dfs2(v,0,0);
            ans++;
        }
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&s,&k);
        memset(head,0,sizeof(head));
        memset(edge,0,sizeof(edge));
        memset(done,0,sizeof(done));
        t=ans=0;
        for(int i=1;i<=n;i++) d[i].clear();
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        dfs1(s,0);
        solve();
        printf("%d\n",ans);
    }
    return 0;
}

例题16 Beijing Guards

思路比较巧妙的一个题,我们考虑奇数情况的时候显然就是编号为奇数的人尽可能向前取,编号为偶数的人尽可能往后取。
但是偶数的时候因为环的存在,我们需要考虑每个人执行上述贪心策略,是否可行。
所以我们用二分来把这个最优化问题,转化为判定性问题。
记录每个人在\([1,a1]\)的范围内取了几个,在\([a1+1,n]\)范围内取了几个(分别用\(ll[i],rr[i]\)来表示),最后判断第n个人在\([1,r1]\)里面是否取出数即可,如果有取出,那么就扩大l。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int T,n;
int a[MAXN],ll[MAXN],rr[MAXN];
inline bool check(int limit)
{
    int x=a[1],y=limit-a[1];
    ll[1]=x,rr[1]=0;
    for(int i=2;i<=n;i++)
    {
        if(i%2==1)
        {
            rr[i]=min(y-rr[i-1],a[i]);
            ll[i]=a[i]-rr[i];
        }
        else
        {
            ll[i]=min(x-ll[i-1],a[i]);
            rr[i]=a[i]-ll[i];
        }
    }
    if(ll[n]==0) return true;
    else return false;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        if(n==1){printf("%d\n",a[1]);continue;}
        a[n+1]=a[1];
        int l=0,r=0;
        for(int i=1;i<=n;i++) l=max(l,a[i]+a[i+1]);
        if(n%2==0) printf("%d\n",l);
        else
        {
            for(int i=1;i<=n;i++) r=max(r,a[i]*3);
            while(l<r)
            {
                int mid=(l+r)>>1;
                if(check(mid)) r=mid;
                else l=mid+1;
            }
            printf("%d\n",l);
        }
    }
    return 0;
}

例题17 Age sort

桶排

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 110
using namespace std;
int n;
int cnt[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        memset(cnt,0,sizeof(cnt));
        int cur;
        for(int i=1;i<=n;i++) scanf("%d",&cur),cnt[cur]++;
        bool flag=false;
        for(int i=1;i<=100;i++)
        {
            if(cnt[i]==0) continue;
            for(int j=1;j<=cnt[i];j++)
            {
                if(flag==true) printf(" ");
                printf("%d",i);
                flag=true;
            }
        }
        printf("\n");
    }
    return 0;
} 

例题18 Open Credit System

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,T;
int a[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int ans=-2147483647;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int maxx=-2147483647;
        for(int i=1;i<=n;i++)
        {
            if(i!=1) ans=max(ans,maxx-a[i]);
            maxx=max(maxx,a[i]);
            // printf("maxx=%d ans=%d\n",maxx,ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}

例题19 Calculator Conundrum

Floyd判圈算法。用来处理带循环节的相关问题,可以在线性的时间内判定是否有环,如果选择1和2为前进步数的话,还可以算出环上的每一个元素。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,k,ans,T;
int s[MAXN];
inline int calc(int x)
{
    long long cur_ans=1ll*x*x;
    int cnt=0;
    while(cur_ans)
    {
        s[++cnt]=cur_ans%10;
        cur_ans/=10;
    }
    cur_ans=0;
    for(int i=cnt,j=1;i>=0&&j<=n;j++,i--)
        cur_ans=cur_ans*10+s[i];
    return cur_ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        ans=k;
        int k1=k,k2=k;
        do
        {
            k1=calc(k1);
            k2=calc(k2);
            if(k2>ans) ans=k2;
            k2=calc(k2);
            if(k2>ans) ans=k2; 
        }while(k1!=k2);
        printf("%d\n",ans);
    }
    return 0;
}

例题20 Metor

一维扫描线维护信息
(但是蓝书上写的预处理l,r区间的方法真的是好qwq)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#define MAXN 200010
using namespace std;

struct lin{
    double x;
    int type;
    bool operator<(const lin &a) const{
        return x<a.x||(x==a.x&&type>a.type);
    }
}line[MAXN];

void updat(int x,int a,int w,double &l,double &r)
{
    if(!a){
        if(x<=0||x>=w)
            r=l-1;
    }
    else
        if(a>0){
            l=max(l,-(double)x/a);
            r=min(r,(double)(w-x)/a);
        }
        else{
            l=max(l,(double)(w-x)/a);
            r=min(r,-(double)x/a);
        }
}

int main(){
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    int t;
    scanf("%d",&t);
    while(t--){
        int w,h,n,e=0; 
        scanf("%d%d%d",&w,&h,&n);
        for(int i=0;i<n;i++){
            int x,y,a,b;
            scanf("%d%d%d%d",&x,&y,&a,&b);
            double l=0,r=1e9;
            updat(x,a,w,l,r);
            updat(y,b,h,l,r);
            if(r>l){ 
                line[e]=(lin){l,0},e++;
                line[e]=(lin){r,1},e++;
            }
        }
        sort(line,line+e);
        int cnt=0,ans=0;
        for(int i=0;i<e;i++){ 
            if(line[i].type==0){
                cnt++; 
                ans=max(ans,cnt); 
            }
            else cnt--; 
        }
        printf("%d\n",ans);
    }
    return 0;
}

例题21 Subsequence

\(O(n^2)\)化简到O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,s;
int a[MAXN];
int main()
{
    while(scanf("%d%d",&n,&s)==2)
    {
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int l=1,r=0,ans=0,minn=2147483647;
        for(int i=1;i<=n;i++)
        {
            ans+=a[i];r++;
            if(ans>=s)
            {
                while(ans-a[l]>=s&&l<r) ans-=a[l],l++;
                minn=min(minn,r-l+1);
            }
        }
        if(minn==2147483647) printf("0\n");
        else printf("%d\n",minn);
    }
    return 0;
}

例题22 City Games

请注意这个题的矩阵中其实有空格。。。
悬线法模板

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define MAXN 1010
using namespace std;
int T,n,m,ans;
int up[MAXN][MAXN],r[MAXN][MAXN],l[MAXN][MAXN];
char a[MAXN][MAXN],s[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        ans=0;
        for(int i=1;i<=n;++i)
            for(int j=0;j<m;++j)
            {
                int ch=getchar();
                while(ch!='F'&&ch!='R') ch=getchar();
                a[i][j+1]=ch;
            }
        for(int i=1;i<=n;i++)
        {
            int ll=0,rr=m+1;
            for(int j=1;j<=m;j++)
            {
                if(a[i][j]!='F') {up[i][j]=l[i][j]=0;ll=j;}
                else           
                {
                    up[i][j]=(i==1)?1:up[i-1][j]+1;
                    l[i][j]=(i==1)?ll+1:max(l[i-1][j],ll+1);
                }
            }
            for(int j=m;j>=1;j--)
            {
                if(a[i][j]!='F'){r[i][j]=m+1;rr=j;}
                else
                {
                    r[i][j]=(i==1)?rr-1:min(r[i-1][j],rr-1);
                    ans=max(ans,up[i][j]*(r[i][j]-l[i][j]+1));
                }
            }
        }
        printf("%d\n",ans*3);
    }
    return 0;
}

例题23 Distant Galaxy

部分枚举 可以有效地降低复杂度qwq
运用了子序列这道例题里面提到的将枚举两个端点化简到\(O(n)\)复杂度的求最值问题的小技巧

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,cnt,sum,ans;
struct Node{int x,y;}node[MAXN];
int y[MAXN],le[MAXN],up1[MAXN],up2[MAXN];
inline bool cmp(struct Node x,struct Node y){return x.x<y.x;}
inline int solve()
{
    sort(&node[1],&node[n+1],cmp);
    sort(&y[1],&y[n+1]);
    int tot=unique(&y[1],&y[n+1])-y-1;
    if(tot<=2) return n;
    int cur_ans=0;
    for(int i=1;i<=tot;i++)
    {
        for(int j=i+1;j<=tot;j++)
        {
            int y_down=y[i],y_up=y[j];
            sum=0;
            for(int k=1;k<=n;k++)
            {
                if(k==1||node[k].x!=node[k-1].x)
                {
                    sum++;
                    up1[sum]=up2[sum]=0;
                    le[sum]=le[sum-1]+up2[sum-1]-up1[sum-1];
                }
                if(node[k].y>y_down&&node[k].y<y_up) up1[sum]++;
                if(node[k].y>=y_down&&node[k].y<=y_up) up2[sum]++;
            }
            if(sum<=2) return n;
            int maxx=0;
            for(int k=1;k<=sum;k++)
            {
                ans=max(ans,le[k]+up2[k]+maxx);
                maxx=max(maxx,up1[k]-le[k]);
            }
        }
    } 
    return ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)==1)
    {
        if(n==0) break;
        for(int i=1;i<=n;i++) scanf("%d%d",&node[i].x,&node[i].y),y[i]=node[i].y;
        ans=0;
        cnt++;
        printf("Case %d: %d\n",cnt,solve());
    }
    return 0;
}

例题24 Garbage Heap

\(O(n^5)\)计算最大子立方体(不一定非要是正方体qwq)
一维枚举套二维前缀和qwq

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXN 25
using namespace std;
int a,b,c,T;
long long ans;
long long val[MAXN][MAXN][MAXN],sum[MAXN][MAXN][MAXN][MAXN],pre_max[MAXN][MAXN][MAXN][MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&a,&b,&c);
        for(int i=1;i<=a;i++)
            for(int j=1;j<=b;j++)
                for(int k=1;k<=c;k++)
                    scanf("%lld",&val[i][j][k]);
    memset(sum,0,sizeof(sum));
    memset(pre_max,0,sizeof(pre_max));
        ans=-INF;
        for(int k=1;k<=a;k++)
        {
            for(int i=1;i<=b;i++)
            {
                for(int j=i;j<=b;j++)
                {
                    for(int p=1;p<=c;p++)
                    {
                        long long cur_ans=0;
                        for(int q=p;q<=c;q++)
                        {
                            cur_ans+=val[k][j][q];
                            sum[i][j][p][q]=sum[i][j-1][p][q]+cur_ans;
                            if(k==1) pre_max[i][j][p][q]=sum[i][j][p][q];
                            else pre_max[i][j][p][q]=max(pre_max[i][j][p][q]+sum[i][j][p][q],sum[i][j][p][q]);
                            ans=max(ans,pre_max[i][j][p][q]);
                        }
                    }
                }
            }
        }
        printf("%lld\n",ans);
        if(T) printf("\n");
    }
    return 0;
}

例题25 Jurassic Remains

折半枚举

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define MAXN 100010
using namespace std;
int n,ans;
int sum[MAXN];
char s[MAXN];
map<int,int>ex;
inline int bitcount(int x){return x==0?0:bitcount(x/2)+(x&1);}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)!=EOF)
    {
        ex.clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            sum[i]=0;
            for(int j=0,len=strlen(s);j<len;j++) sum[i]^=(1<<(s[j]-'A'));
        }
        for(int i=0;i<(1<<(n/2));i++)
        {
            int cur_ans=0;
            for(int j=0;j<(n/2);j++)
            {
                if(i&(1<<j))
                    cur_ans^=sum[j+1];
            }
            if(!ex.count(cur_ans)||bitcount(ex[cur_ans])<bitcount(i)) ex[cur_ans]=i;
        }
        int n1=n/2,n2=n-n1;
        ans=0;
        for(int i=0;i<(1<<(n2));i++)
        {
            int cur_ans=0;
            for(int j=0;j<(n2);j++)
            {
                if(i&(1<<j))
                    cur_ans^=sum[n1+j+1];
            }
            if(ex.count(cur_ans)&&bitcount(ans)<bitcount(ex[cur_ans])+bitcount(i)) 
                ans=(i<<n1)^ex[cur_ans];
                // ans=(ex[cur_ans]<<n1)^i;
        }
        printf("%d\n",bitcount(ans));
        for(int i=0;i<n;i++) 
            if(ans&(1<<i))
                printf("%d ",i+1);
        printf("\n");
    }
    return 0;
}

例题26 And Then There Was One

普通的约瑟夫问题,递推公式是\(dp[i]=(dp[i-1]+k)%i\)(注意,这个版本是从0开始编号的,如果求的是从1开始编号,那么应该最后答案是dp[n]+1)
第一个删除m,那意味着从\(m-k\)开始,所以最终答案是\((m-k+1+f[n])%n\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int T,n,m,k;
int dp[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d%d%d",&n,&k,&m)==3)
    {
        if(n==0) break;
        dp[1]=0;
        for(int i=2;i<=n;i++) dp[i]=(dp[i-1]+k)%i;
        int ans=(m-k+1+dp[n])%n;
        while(ans<=0) ans+=n;
        printf("%d\n",ans);
    }
    return 0;
}

例题27 Prince ans Princess

如何在\(O(nlogn)\)的时间内算出A和B的最长公共子序列?
正解是将B转换成其数值在A中出现的位置,比如说A 1 5 2 4 3,B 5 4 2 1 3,B转换成2 4 3 1 5.
然后求最长上升子序列长度即可qwq

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
int T,n,p,q,ans,tot;
int num[MAXN],b[MAXN],dp[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        tot++;
        scanf("%d%d%d",&n,&p,&q);
        memset(num,0,sizeof(num));
        for(int i=1;i<=p+1;i++)
        {
            int cur;
            scanf("%d",&cur);
            num[cur]=i;
        }
        n=0;
        for(int i=1;i<=q+1;i++)
        {
            int cur;
            scanf("%d",&cur);
            if(num[cur]) b[++n]=num[cur];
        }
        int cnt=0;
        dp[0]=-1;
        for(int i=1;i<=n;i++)
        {
            if(b[i]>dp[cnt]) dp[++cnt]=b[i];
            else
            {
                int pos=lower_bound(&dp[1],&dp[cnt+1],b[i])-dp;
                dp[pos]=b[i];
            }
        }
        printf("Case %d: %d\n",tot,cnt);
    }
    return 0;
}

例题28 Game of Sum

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1010
using namespace std;
int n;
int a[MAXN],g[MAXN][MAXN],f[MAXN][MAXN],d[MAXN][MAXN],sum[MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)==1)
    {
        if(!n) break;
        sum[0]=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
        for(int i=1;i<=n;i++) g[i][i]=d[i][i]=f[i][i]=a[i];
        for(int l=1;l<n;l++)   
            for(int i=1;i+l<=n;i++)
            {
                int j=i+l;
                int cur_ans=0;
                cur_ans=min(cur_ans,f[i+1][j]);
                cur_ans=min(cur_ans,g[i][j-1]);
                d[i][j]=sum[j]-sum[i-1]-cur_ans;
                f[i][j]=min(d[i][j],f[i+1][j]);
                g[i][j]=min(d[i][j],g[i][j-1]);
            }
        printf("%d\n",2*d[1][n]-sum[n]);
    }
    return 0;
}

例题29 Hacker's Crackdown

状压DP

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 17
using namespace std;
int n,cnt;
int con[1<<MAXN],cover[1<<MAXN],dp[1<<MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    while(scanf("%d",&n)==1)
    {
        if(n==0) break;
        cnt++;
        for(int i=1;i<=n;i++)
        {
            int k,cur;
            scanf("%d",&k);
            con[i]=(1<<(i-1));
            while(k--) scanf("%d",&cur),con[i]|=(1<<cur);
        }
        memset(cover,0,sizeof(cover));
        for(int i=0;i<(1<<n);i++)
        {
            for(int j=0;j<n;j++)
                if(i&(1<<j))
                    cover[i]|=con[j+1];
        }
        memset(dp,0,sizeof(dp));
        for(int i=0;i<(1<<n);i++)
        {
            for(int s0=i;s0;s0=(s0-1)&i)
            {
                if(cover[s0]==(1<<n)-1)
                    dp[i]=max(dp[i],dp[i^s0]+1);
            }
        }
        printf("Case %d: %d\n",cnt,dp[(1<<n)-1]);
    }
    return 0;
}

例题30 Placing Lampposts

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int T,n,m,t;
int head[MAXN<<1],dp[MAXN][2],sum[MAXN][2],done[MAXN];
struct Edge{int nxt,to;}edge[MAXN<<1];
inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;}
inline void dfs(int x,int pre)
{
    dp[x][1]=1;
    dp[x][0]=0;
    done[x]=1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==pre) continue;
        dfs(v,x);
        // printf("x=%d v=%d\n",x,v);
        dp[x][0]+=dp[v][1];
        sum[x][0]+=sum[v][1];
        if(dp[v][1]<dp[v][0])
        {
            dp[x][1]+=dp[v][1];
            sum[x][1]+=sum[v][1]+1;
        }
        else if(dp[v][0]<dp[v][1])
        {
            dp[x][1]+=dp[v][0];
            sum[x][1]+=sum[v][0];
        }
        else
        {
            dp[x][1]+=dp[v][1];
            sum[x][1]+=max(sum[v][1]+1,sum[v][0]);
        }
    }
    // printf("dp[%d][0]=%d dp[%d][1]=%d\n",x,dp[x][0],x,dp[x][1]);
    // printf("sum[%d][0]=%d sum[%d][1]=%d\n",x,sum[x][0],x,sum[x][1]);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&T);
    while(T--)
    {
        t=0;
        memset(head,0,sizeof(head));                   
        memset(edge,0,sizeof(edge));
        memset(done,0,sizeof(done));
        memset(sum,0,sizeof(sum));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        int ans1=0,ans2=0;
        for(int i=0;i<n;i++)
        {
            if(done[i]==1) continue;
            dfs(i,-1);
            if(dp[i][0]>dp[i][1])
            {
                ans1+=dp[i][1];
                ans2+=sum[i][1];
            }
            else if(dp[i][0]<dp[i][1])
            {
                ans1+=dp[i][0];
                ans2+=sum[i][0];
            }
            else
            {
                ans1+=dp[i][1];
                ans2+=max(sum[i][1],sum[i][0]);
            }
        }
            printf("%d %d %d\n",ans1,ans2,m-ans2);
    }
    return 0;
}

例题31 Robotruck

例题32 Sharing Chocolate

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 110
#define LOG 16
using namespace std;
int n,X,Y,tim;
int a[MAXN],dp[MAXN][1<<LOG],done[MAXN][1<<LOG],sum[1<<LOG];
inline int calc(int x){return x==0?0:calc(x/2)+(x&1);}
inline int search(int x,int s)
{
    if(done[x][s]) return dp[x][s];
    done[x][s]=1;
    if(calc(s)==1) 
    {
        dp[x][s]=1;
        return 1;
    }
    for(int s1=(s-1)&s;s1;s1=(s1-1)&s)
    {
        int s2=s-s1;
        if(sum[s1]%x==0&&search(min(x,sum[s1]/x),s1)&&search(min(x,sum[s2]/x),s2))
        {
            dp[x][s]=1;
            return 1;
        }
        int y=sum[s]/x;
        if(sum[s1]%y==0&&search(min(y,sum[s1]/y),s1)&&search(min(sum[s2]/y,y),s2)) 
        {
            dp[x][s]=1;
            return 1;
        }
    }
    dp[x][s]=0;
    return 0;
}
int main()
{
    while(scanf("%d",&n)==1)
    {
        if(n==0) break;
        ++tim;
        memset(done,0,sizeof(done));
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        int maxx=(1<<n)-1,tot=0;
        scanf("%d%d",&X,&Y);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=0;i<=maxx;i++)
            for(int j=0;j<n;j++)
                if(i&(1<<j))
                    sum[i]+=a[j+1];
        int ans=0;
        if(sum[maxx]!=X*Y||sum[maxx]%X!=0) ans=0;
        else ans=search(min(X,Y),maxx);
        printf("Case %d: %s\n",tim,ans==0?"No":"Yes");
    }
    return 0;
}

转载于:https://www.cnblogs.com/fengxunling/p/10681071.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值