A. Sea Battle
题意
给两个矩形堆在一起,大的在下,小的在上,左边界对齐,求整个图形的外延有多少格。如图一
答案显然就是最长的宽x2+两个矩形的高x2+4
B. Draw!
题意
给出部分足球比赛的比分,每次进求,赢得一方比分只能加1,可能出现多次相同的比分,最后一个比分为最终得分。求比赛期间出现平局情况的最多次数(就是比分相同的情况)。
思路
先预处理一下,把重复的比分去掉。对于相邻两次比分,(a[i],b[i])—>(a[i+1],b[i+1])期间可能出现最多平局的次数就是线段【a[i],a[i+1]】与【b[i],b[i+1]】的交集的大小。注意一个坑点就是如果a[i]==b[i],那么a[i]这个点会被前后重复计算,所以要减一。但是对于最后的ans要加1,因为(0,0)的时候没有被多算.
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
int n;
int a[maxn][2],b[maxn][2],tot;
int calc(int x1,int y1,int x2,int y2)
{
if(x1>x2){
swap(x1,x2);
swap(y1,y2);
}
if(y1>=x2&&y1<=y2)return y1-x2+1;
else if(y1>y2)return y2-x2+1;
else return 0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i][0],&a[i][1]);
for(int i=1;i<=n;i++){
if(a[i][0]==a[i-1][0]&&a[i][1]==a[i-1][1])continue;
else {
b[++tot][0]=a[i][0];
b[tot][1]=a[i][1];
}
}
int ans=0;
for(int i=1;i<=tot;i++){
ans+=calc(b[i-1][0],b[i][0],b[i-1][1],b[i][1]);
if(b[i-1][0]==b[i-1][1])ans--;
//printf("ans = %d\n",ans);
}
printf("%d\n",++ans);
return 0;
}
C. Birthday
题意
给n个不同高度的人排一下队,且排成一个环形。要使相邻两个人的高度差的最大值最小。
思路
求最大的最小,一般都可以二分。这里可以直接排序,对于第一个人,将其余比他高的人依次放在他左右两侧即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
int n;
int a[maxn];
int b[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+n+1);
int mid=1000;
int l=mid,r=mid;
b[mid]=a[1];
for(int i=2;i<=n;i++){
if(i%2==0)b[++r]=a[i];
else b[--l]=a[i];
}
for(int i=l;i<=r;i++)printf("%d%c",b[i],i==r?'\n':' ');
return 0;
}
D. Gourmet choice
题意
一个美食家第一天吃了n中美食,第二天吃了m种。他画了一个nxm的表格来表示他们之间好吃程度的大小关系。a[i][j]=’>‘表示i比j好吃。a[i][j]=’<‘表示j比i好吃。a[i][j]=’='表示i,j的好吃程度一样。现在要你给所有的菜打个分数。使得满足该表中的所有关系,并且要求最高的分数越小越好。
思路
对于’=‘的关系先建图,搜连通块,缩点。对于’<‘和’>‘关系再建一个有向图,方向由低级的指向高级;例如a[u][v]=’>’ ,v==>u;然后将所有入度为0的点入度,设dis[u]=1;
然后类似跑一遍spfa,找出最远的点。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 2005
int n,m;
char s[maxn][maxn];
int col[maxn],color;
vector<int>c[maxn];
vector<int>e[maxn],ne[maxn];
bool vis[maxn],inq[maxn];
int dis[maxn],du[maxn],cnt[maxn];
void dfs(int x)
{
vis[x]=1;
col[x]=color;
c[color].push_back(x);
for(int i=0;i<e[x].size();i++){
int v=e[x][i];
if(vis[v]==0){
dfs(v);
}
}
return ;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s[i]+1);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]=='='){
e[i].push_back(n+j);
e[n+j].push_back(i);
}
}
}
for(int i=1;i<=n+m;i++){
if(vis[i]==0){
++color;
dfs(i);
}
}
bool flag=0;
for(int i=1;i<=n;i++){
if(flag)break;
for(int j=1;j<=m;j++){
if(flag)break;
if(s[i][j]=='<'){
if(col[i]!=col[n+j]){
ne[col[i]].push_back(col[n+j]);
du[col[n+j]]++;
}
else flag=1;
}
else if(s[i][j]=='>'){
if(col[i]!=col[n+j]){
ne[col[n+j]].push_back(col[i]);
du[col[i]]++;
}
else flag=1;
}
}
}
if(flag){
printf("No\n");
return 0;
}
queue<int>q;
for(int i=1;i<=color;i++){
if(du[i]==0){
q.push(i);
inq[i]=1;
dis[i]=1;
}
}
int ma=0;
while(!q.empty()){
int u=q.front();q.pop();inq[u]=0;
cnt[u]++;
if(cnt[u]>=n+m){
flag=1;
break;
}
ma=max(ma,dis[u]);
for(int i=0;i<ne[u].size();i++){
int v=ne[u][i];
if(dis[v]==0){
q.push(v);
dis[v]=dis[u]+1;
}
else if(dis[v]<dis[u]+1){
dis[v]=dis[u]+1;
if(inq[v]==0){
inq[v]=1;
q.push(v);
}
}
}
}
if(flag){
printf("No\n");
return 0;
}
printf("Yes\n");
for(int i=1;i<=n;i++)printf("%d%c",dis[col[i]],i==n?'\n':' ');
for(int i=1;i<=m;i++)printf("%d%c",dis[col[i+n]],i==m?'\n':' ');
return 0;
}
E. String Multiplication
题意
定义字符串S∗T=T+S1(第一个字母)+T+…+Sn+TS∗T=T+S1(第一个字母)+T+…+Sn+T ST=T+S_1(第一个字母)+T+…+S_n+TS∗T=T+S1(第一个字母)+T+…+Sn+T,例如ac∗b=babcbac∗b=babcb acb=babcbac∗b=babcb,现在给出n个串P1…PnP1…Pn P_1…P_nP1…Pn,求目标串(…(P1∗P2)∗P3)…)∗Pn(…(P1∗P2)∗P3)…)∗Pn (…(P_1*P_2)*P_3)…)*P_n(…(P1∗P2)∗P3)…)∗Pn中,连续相同字符的个数的最大值(abbbc=3abbbc=3 abbbc=3abbbc=3)。
(参考博客
https://blog.csdn.net/jk_chen_acmer/article/details/87904929
思路
枚举26个字母的最长长度,然后取最大。对于每一个字母,s[i].L表示第i个字符串的前缀最多有多少个该字母,s[i].R表示其后缀最多有多少个该字母,s[i].ma表示第i个串中包含连续的该字符的最长长度。
如果s[i]全是由该字符组成,那么dp[i]=dp[i-1]+dp[i-1]*strlen(s[i])
如果dp[i-1]!=0,dp[i]=max(s[i].ma,s[i].L+s[i].R+1)
如果dp[i-1]==0,dp[i]=s[i].ma;
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
int n;
string s[maxn];
struct node{
int l,r,mid;
bool isall;
};
node a[maxn][30];
int dp[maxn][30];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
cin>>s[i];
}
//for(int i=1;i<=n;i++)cout<<s[i]<<endl;
for(int i=1;i<=n;i++){
int len=s[i].size();
for(int j=0;j<26;j++){
for(int k=0;k<len;k++){
if('a'+j==s[i][k])a[i][j].l++;
else break;
}
for(int k=len-1;k>=0;k--){
if('a'+j==s[i][k])a[i][j].r++;
else break;
}
int temp=0;
for(int k=0;k<len;k++){
if('a'+j==s[i][k])temp++;
else{
a[i][j].mid=max(a[i][j].mid,temp);
temp=0;
}
}
a[i][j].mid=max(a[i][j].mid,temp);///最后记得判断一次 因为可能结尾满足 经常有这种bug
if(temp==len)a[i][j].isall=1;
}
}
/*for(int i=1;i<=n;i++){
printf("--------------i = %d:\n",i);
for(int j=0;j<26;j++){
printf("%3d: l = %d r = %d mid = %d isall = %d\n",j,a[i][j].l,a[i][j].r,a[i][j].mid,a[i][j].isall);
}
}*/
for(int i=0;i<26;i++){
for(int j=1;j<=n;j++){
if(a[j][i].isall)dp[j][i]=dp[j-1][i]+(dp[j-1][i]+1)*a[j][i].mid;
else if(dp[j-1][i]!=0)dp[j][i]=max(a[j][i].mid,a[j][i].l+a[j][i].r+1);
else if(dp[j-1][i]==0)dp[j][i]=a[j][i].mid;
}
}
int ans=0;
for(int i=0;i<26;i++)ans=max(ans,dp[n][i]);
printf("%d\n",ans);
return 0;
}
F. Asya And Kittens
题意
有一个长度为n的序列,进行了n-1次合并操作,终于变成了一个区域。每次合并只能选择相邻两个区域内的数进行。给出合并顺序,要求还原出原序列。
思路
用两个并查集维护每个数字所在区间的最左点和最右点。每次选择两个数字合并的时候,就将他们的最右点和最左点连起来,同时更新并查集。最后将链条从头搜到尾即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 150005
int n;
vector<int>e[maxn];
int du[maxn];
int king1[maxn],king2[maxn];
vector<int>ans;
void init()
{
for(int i=0;i<maxn;i++)king1[i]=king2[i]=i;
}
int getfa1(int x){return king1[x]==x?x:king1[x]=getfa1(king1[x]);}///最前面的值
int getfa2(int x){return king2[x]==x?x:king2[x]=getfa2(king2[x]);}///最后面的值
int main()
{
init();
scanf("%d",&n);
for(int i=1,x,y;i<=n-1;i++){
scanf("%d%d",&x,&y);
int tx=getfa2(x);
int ty=getfa1(y);
king1[ty]=tx;
king2[tx]=ty;
e[tx].push_back(ty);
du[ty]++;
}
int s=0;
for(int i=1;i<=n;i++){
if(du[i]==0){
s=i;
break;
}
}
int i;
for(i=s;e[i].size();i=e[i][0]){
printf("%d ",i);
}
printf("%d\n",i);
return 0;
}