思路:贪心,对于报出x的兔子来说,可以把x+1个组成一组,有不足x+1个的就补齐。
#include
#define int long long
#define MAXN 2000005
using namespace std;
int a[1005];
signed main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)
{
int t;cin>>t;
a[t]++;
}
int ans=0;
for(int i=0;i<=1000;i++)
{
ans+=a[i];
if(a[i]%(i+1))
ans+=i+1-a[i]%(i+1);
}
cout<<ans<<endl;
}
思路:栈匹配,每次成功匹配之后就会把匹配上的括号对拿走,那么可以通过栈顶没匹配的括号的下标知道当前这次匹配正确匹配上的所有括号的总长度。
#include
#define int long long
#define MAXN 2000005
using namespace std;
int a[1005];
signed main()
{
string s;cin>>s;
stack<int> stk;
stk.push(-1);
int ans=0;
for(int i=0; i<s.size(); i++)
{
if(stk.size()>1&&s[i]==')'&&s[stk.top()]=='(')
{
stk.pop();
ans=max(ans,i-stk.top());
continue;
}
stk.push(i);
}
cout<<ans<<endl;
}
思路:可以知道如果在二分图上做dfs的话每走一步都会从当前集合跳转到另一个集合,所以按照走的步数的奇偶性对图染色即可,如果染色没有出现冲突说明就是二分图。
#include
#define int long long
#define MAXN 505
using namespace std;
vector<int> edge[MAXN];
int col[MAXN];
int ans=1;
void dfs(int now,int p)
{
if(col[now]!=-1)
{
if(col[now]!=p)
ans=0;
return;
}
col[now]=p;
for(int i=0;i<edge[now].size();i++)
dfs(edge[now][i],1-p);
}
void solve()
{
ans=1;
memset(col,-1,sizeof(col));
for(int i=0;i<MAXN;i++)
edge[i].clear();
int n,m;cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
}
for(int i=1;i<=n;i++)
if(col[i]==-1)
dfs(i,0);
if(ans)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
signed main()
{
int T;cin>>T;
while(T--)
solve();
}
思路:完全背包板子
#include
#define int long long
#define MAXN 200005
using namespace std;
int dp[MAXN];
int v[MAXN],c[MAXN];
signed main()
{
int n,x;cin>>n>>x;
for(int i=1;i<=n;i++)
cin>>v[i]>>c[i];
for(int i=1;i<=n;i++)
for(int j=v[i];j<=x;j++)
dp[j]=max(dp[j],dp[j-v[i]]+c[i]);
cout<<dp[x]<<endl;
}
思路:dfs回溯直接枚举每个格子填什么数字,但是需要做剪枝。每次枚举一个格子内的数字后,检查同行同列和同属的九宫格看是否没冲突,是合法的填法,不合法就回溯。同时如果找到超过1个方案的时候不再搜索直接返回,即可通过这题。
#include
#define int long long
#define MAXN 200005
using namespace std;
int a[10][10];
int out[10][10];
int cnt=0;
bool check(int x,int y)
{
for(int i=1; i<=9; i++)
if(a[i][y]==a[x][y]&&i!=x
||a[x][i]==a[x][y]&&i!=y)
return false;
int bx=(x-1)/3*3+1,by=(y-1)/3*3+1;
for(int i=0; i<=2; i++)
for(int j=0; j<=2; j++)
if(a[bx+i][by+j]==a[x][y]&&((bx+i)!=x||(by+j)!=y))
return false;
return true;
}
void dfs(int x,int y)
{
if(cnt>1)return;
if(x==10)
{
if(y==9)
{
for(int i=1; i<=9; i++)
for(int j=1; j<=9; j++)
out[i][j]=a[i][j];
cnt++;
return;
}
x=1,y++;
}
if(a[x][y]&&check(x,y))
dfs(x+1,y);
else
{
for(int i=1; i<=9; i++)
{
a[x][y]=i;
if(check(x,y))
dfs(x+1,y);
}
a[x][y]=0;
}
}
signed main()
{
for(int i=1; i<=9; i++)
for(int j=1; j<=9; j++)
cin>>a[i][j];
dfs(1,1);
if(cnt!=1)cout<<"No Solution"<<endl;
else
{
for(int i=1; i<=9; i++)
{
for(int j=1; j<=9; j++)
cout<<out[i][j]<<' ';
cout<<endl;
}
}
}
思路:可以知道按照冒泡排序的排序方式是交换次数最少的,而且每次交换使得数组内逆序对数量-1,因此求逆序对即可,这里用归并排序求法,也有离散化+树状数组求法,但是后者不在校招考纲内。
#include
#define int long long
#define MAXN 200005
using namespace std;
int a[MAXN],b[MAXN];
int ans=0;
void merg(int l,int r)
{
if(l==r)return;
int mid=(l+r)/2;
merg(l,mid);
merg(mid+1,r);
int p1=l,p2=mid+1,p3=p1;
while(p1<=mid&&p2<=r)
{
if(a[p1]<=a[p2])
b[p3++]=a[p1++];
else
{
ans+=mid+1-p1;
b[p3++]=a[p2++];
}
}
while(p1<=mid) b[p3++]=a[p1++];
while(p2<=r) b[p3++]=a[p2++];
for(int i=l; i<=r; i++)a[i]=b[i];
}
signed main()
{
int n;cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
merg(1,n);
cout<<ans<<endl;
}
思路:双指针模板题,注意如何写的清晰。
#include
#define int long long
#define MAXN 500005
using namespace std;
int a[MAXN];
signed main()
{
int s,n;cin>>s>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
int ans=0x3f3f3f3f3f3f3f3f;
int L=1,sum=0;
for(int R=1;R<=n;R++)
{
sum+=a[R];
while(sum-a[L]>=s)
sum-=a[L++];
if(sum>=s)
ans=min(ans,R-L+1);
}
if(ans!=0x3f3f3f3f3f3f3f3f)
cout<<ans<<endl;
else cout<<0<<endl;
}
思路:岛屿数量一样的题,泛洪算法。
#include
#define int long long
#define MAXN 500005
using namespace std;
char mp[1005][1005];
void dfs(int x,int y)
{
if(mp[x][y]!='W')return;
mp[x][y]='.';
for(int i=-1;i<=1;i++)
for(int j=-1;j<=1;j++)
dfs(x+i,y+j);
}
signed main()
{
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>mp[i][j];
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(mp[i][j]=='W')
{
ans++;
dfs(i,j);
}
cout<<ans<<endl;
}
思路:meet in middle,30个数可以分两半分别枚举是否选取,枚举 2 15 2^{15} 215 次就行,这边用了状态压缩的枚举方法。
#include
#define int long long
#define MAXN 500005
using namespace std;
int a[35];
int getsum(int x,int b)
{
int ret=0;
for(int i=0;i<20;i++)
if((x>>i)&1)
ret+=a[i+b];
return ret;
}
int getmin(int a,int b)
{
for(int i=0;i<35;i++)
if(((a>>i)&1)!=((b>>i)&1))
return (a>>i)&1?a:b;
return a;
}
unordered_map<int,int> L;
signed main()
{
int n,m;cin>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i];
sort(a,a+n);
for(int i=0;i<(1<<(n/2));i++)
{
int now=getsum(i,0);
if(!L.count(now))L[now]=i;
else L[now]=getmin(L[now],i);
}
int ans=0;
for(int i=0;i<(1<<(n-n/2));i++)
{
int now=getsum(i,n/2);
if(now==m)ans=getmin(ans,(i<<n/2));
else if(L.count(m-now))
ans=getmin(ans,L[m-now]+(i<<n/2));
}
if(ans==0)
cout<<"No solution"<<endl;
else
for(int i=0;i<35;i++)
if((ans>>i)&1)
cout<<a[i]<<' ';
}