可是我为什么要专门开一发写这个呢......
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;
}