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

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

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;
}
posted @ 2019-04-10 08:11  风浔凌  阅读(406)  评论(5编辑  收藏  举报