这是一份做完题一万年以后写的题解。。
Day 1 T1 神奇的幻方
无脑模拟题。。复杂度 O(N 2 ) 。
#include<bits/stdc++.h>
int main()
{
int x,y,n,i,magic[39][39]={0};
scanf("%d",&n);
x=0;
y=(n-1)/2;
for(i=1;i<n*n;i++)
{
magic[x][y]=i;
if (x==0)
{
if (y==n-1)
x++;
else
x=n-1,y++;
}
else
{
if (y==n-1)
x--,y=0;
else
if (magic[x-1][y+1]==0)
x--,y++;
else
x++;
}
}
magic[x][y]=n*n;
for (x=0;x<n;x++)
{
for (y=0;y<=n-1;y++)
printf("%d ",magic[x][y]);
printf("\n");
}
return 0;
}
Day 1 T2 信息传递
题意:给定一个每个点出度都为1的有向图,无重边和自环,求图的最小环。这道题直接用 Tarjan 求强连通分量就好了。因为保证每个点出度都是1,显然每个强连通分量必然不是一个单独的点就是一个环,最后求最小的大于1的环即可。最后要扩栈。复杂度 O(N) 。
#include<bits/stdc++.h>
const int N=200001;
int t[N],dfn[N],low[N],stack[N],index=0,top=0,min=N;
bool instack[N];
void tarjan(int i)
{
int j,num;
instack[i]=true;
stack[++top]=i;
dfn[i]=++index;
low[i]=index;
j=t[i];
if (!dfn[j])
{
tarjan(j);
if(low[j]<low[i])
low[i]=low[j];
}
else
{
if(instack[j] && dfn[j]<low[i])
low[i]=dfn[j];
}
if (dfn[i]==low[i])
{
num=0;
do
{
j=stack[top--];
instack[j]=false;
num++;
}
while (j!=i);
if (num<min && num>1)
min=num;
}
}
void main_main()
{
int i=0,n;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d",&t[i]);
for (i=1;i<=n;i++)
if (!dfn[i])
tarjan(i);
printf("%d",min);
}
const int main_stack=16;
char my_stack[128<<20];
int main()
{
__asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
__asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");
main_main();
__asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
return 0;
}
Day 1 T3 斗地主
这道题用记忆化搜索+状压,注意优先出掉组合牌,单牌最后一次出完,可以减少搜索次数。具体实现的时候可以把组合牌事先枚举出来预处理,然后暴力的基础上稍微优化一下就能过了。复杂度好复杂。。没法算。
#include<bits/stdc++.h>
using namespace std;
map <long long,int> dict;
int four[7],three[9],single[9],ddouble[11],tripple[7];
long long state(int *card)
{
int i;
long long sum=0;
for (i=0;i<=13;i++)
{
sum=sum*5+card[i];
}
return sum;
}
int minimum(int *card)
{
int i,j,k,min=23,time=0;
long long st;
bool flag=false;
st=state(card);
if (st==0)
return 0;
if (dict.count(st))
return dict[st];
for (i=1;i<=four[0];i++)
if (card[four[i]]==4)
{
card[four[i]]-=4;
time=minimum(card)+1;
if (time<min)
min=time;
flag=true;
for (j=0;j<=13;j++)
if (card[j])
{
card[j]--;
for (k=0;k<=13;k++)
if (card[k])
{
card[k]--;
time=minimum(card)+1;
if (time<min)
min=time;
if (card[j]&&card[k])
{
card[j]--;
card[k]--;
time=minimum(card)+1;
if (time<min)
min=time;
card[j]++;
card[k]++;
}
card[k]++;
}
card[j]++;
}
card[four[i]]+=4;
}
for (i=1;i<=three[0];i++)
if (card[three[i]]>=3)
{
card[three[i]]-=3;
for (j=0;j<=13;j++)
if (card[j])
{
card[j]--;
time=minimum(card)+1;
if (time<min)
min=time;
flag=true;
if (card[j])
{
card[j]--;
time=minimum(card)+1;
if (time<min)
min=time;
card[j]++;
}
card[j]++;
}
card[three[i]]+=3;
}
for (i=1;i<=single[0];i++)
if (card[single[i]]&&card[single[i]+1]&&card[single[i]+2]&&card[single[i]+3]&&card[single[i]+4])
{
card[single[i]]--;
card[single[i]+1]--;
card[single[i]+2]--;
card[single[i]+3]--;
card[single[i]+4]--;
time=minimum(card)+1;
if (time<min)
min=time;
for (j=single[i]+5;j<=11;j++)
if (card[j])
{
card[j]--;
time=minimum(card)+1;
if (time<min)
min=time;
}
else
{
card[j]--;
break;
}
flag=true;
if (j>=12)
j=11;
for (k=single[i];k<=j;k++)
card[k]++;
}
for (i=1;i<=ddouble[0];i++)
if (card[ddouble[i]]>=2&&card[ddouble[i]+1]>=2&&card[ddouble[i]+2]>=2)
{
card[ddouble[i]]-=2;
card[ddouble[i]+1]-=2;
card[ddouble[i]+2]-=2;
time=minimum(card)+1;
if (time<min)
min=time;
for (j=ddouble[i]+3;j<=11;j++)
if (card[j]>=2)
{
card[j]-=2;
time=minimum(card)+1;
if (time<min)
min=time;
}
else
{
card[j]-=2;
break;
}
flag=true;
if (j>=12)
j=11;
for (k=ddouble[i];k<=j;k++)
card[k]+=2;
}
for (i=1;i<=tripple[0];i++)
if (card[tripple[i]]>=3&&card[tripple[i]+1]>=3)
{
card[tripple[i]]-=3;
card[tripple[i]+1]-=3;
time=minimum(card)+1;
if (time<min)
min=time;
for (j=tripple[i]+2;j<=11;j++)
if (card[j]>=3)
{
card[j]-=3;
time=minimum(card)+1;
if (time<min)
min=time;
}
else
{
card[j]-=3;
break;
}
flag=true;
if (j>=12)
j=11;
for (k=tripple[i];k<=j;k++)
card[k]+=3;
}
if (!flag)
{
for (i=0;i<=13;i++)
if(card[i])
time++;
if (time<min)
min=time;
}
dict[st]=min;
return min;
}
int main()
{
int card[14],a,b,t,n,i,j,ans[100];
scanf("%d",&t);
scanf("%d",&n);
for (i=0;i<t;i++)
{
memset(card,0,14*sizeof(int));
for (j=0;j<n;j++)
{
scanf("%d%d",&a,&b);
switch (a)
{
case 0:card[13]++;break;
case 1:case 2:card[a+10]++;break;
default:card[a-3]++;break;
}
}
four[0]=0;
three[0]=0;
single[0]=0;
ddouble[0]=0;
tripple[0]=0;
for (j=0;j<=12;j++)
{
if (card[j]==4)
four[++four[0]]=j;
if (card[j]>=3)
three[++three[0]]=j;
}
for (j=0;j<=7;j++)
if (card[j]&&card[j+1]&&card[j+2]&&card[j+3]&&card[j+4])
single[++single[0]]=j;
for (j=0;j<=9;j++)
if (card[j]>=2&&card[j+1]>=2&&card[j+2]>=2)
ddouble[++ddouble[0]]=j;
for (j=0;j<=10;j++)
if (card[i]>=3&&card[i+1]>=3)
tripple[++tripple[0]]=j;
ans[i]=minimum(card);
}
for (i=0;i<t;i++)
printf("%d\n",ans[i]);
return 0;
}
Day 2 T1 跳石头
这是一道典型的最大化最小值的题,可以用二分答案来做。判断答案是否可行时可以用贪心的思想来做,从左到右扫时,距离小于答案的石子一定要先拿掉(否则最小值一定会小于答案),所以只要从左到右不断拿掉这样的石子看次数是否满足要求即可。注意终点的石头不能拿走,要特殊处理。复杂度 O(NlogN) 。
#include<bits/stdc++.h>
const int N=50001;
int main()
{
int l,n,m,i,d[N],d1[N],d2[N],left=0,right,mid,time;
bool flag;
scanf("%d%d%d",&l,&n,&m);
right=l;
for (i=0;i<n;i++)
{
scanf("%d",&d[i]);
}
d1[0]=d2[0]=d[0];
for (i=1;i<=n-1;i++)
d1[i]=d2[i]=d[i]-d[i-1];
d1[n]=d2[n]=l-d[n-1];
while (left<right-1)
{
mid=(left+right)/2;
flag=true;
time=m;
for (i=0;i<n;i++)
{
if (d1[i]<mid)
{
d1[i+1]+=d1[i];
time--;
}
if (time<0)
{
flag=false;
break;
}
}
if (d1[n]<mid)
{
time--;
if (time<0)
flag=false;
}
if (flag)
left=mid;
else
right=mid;
for (i=0;i<=n;i++)
d1[i]=d2[i];
}
if(left==right)
printf("%d",right);
else
{
flag=true;
time=m;
for (i=0;i<n;i++)
{
if (d1[i]<right)
{
d1[i+1]+=d1[i];
time--;
}
if (time<0)
{
flag=false;
break;
}
}
if (d1[n]<right)
{
time--;
if (time<0)
flag=false;
}
if (flag)
printf("%d",right);
else
printf("%d",left);
}
return 0;
}
Day2 T2 子串
DP。设
dp[i][j][k][0]
为
a
串中前
dp[i][j][k][1]=dp[i−1][j−1][k][1]+dp[i][j][k][0]
对于
dp[i][j][k][0]
则要分类讨论。
最后要注意会MLE,所以要用个滚动数组。复杂度 O(nmk)
#include<bits/stdc++.h>
#define N (1001)
#define M (201)
#define K (201)
#define mod (1000000007)
int f[2][M][K][2],i,j,k,n,m,kk,pre=0,now=1;
char a[N],b[M];
int main()
{
memset(f,0,2*M*K*2*sizeof(int));
scanf("%d%d%d\n%s\n%s",&n,&m,&kk,a,b);
f[0][0][0][1]=1;
f[1][0][0][1]=1;
for (i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
for(k=1;k<=kk;k++)
{
if (a[i-1]==b[j-1])
f[now][j][k][0]=(f[pre][j-1][k][0]+f[pre][j-1][k-1][1])%mod;
else
f[now][j][k][0]=0;
f[now][j][k][1]=(f[now][j][k][0]+f[pre][j][k][1])%mod;
}
now=now^pre;
pre=now^pre;
now=now^pre;
}
printf("%d\n",f[pre][m][kk][1]);
return 0;
}
Day2 T3 运输计划
这是一道最小化最大值的题,首先想到二分答案。但是,二分答案以后判断可行性却比较复杂。考虑把所有长度大于答案的路径枚举出来,求出它们边的集,若没有边重叠了
n
次,显然这个答案是不可行的。否则删去最大的这样的边,如果最长路径仍大于答案,则不可行,否则可行。具体实现时,两点之前的最短路径用
#include<bits/stdc++.h>
#define N 300001
typedef struct edge
{
int length,next,to,cnt;
};
typedef struct point
{
int dep,first,fa,siz,son,dis,top,cnt;
};
edge edg[2*N];
point poi[N];
int e=0,max=0,L,LCA[N],u[N],v[N],dis[N],m,n,a[N],b[N],t[N];
void addedg(int a,int b,int l)
{
edg[++e].next=poi[a].first;
edg[e].length=l;
edg[e].to=b;
poi[a].first=e;
edg[++e].next=poi[b].first;
poi[b].first=e;
edg[e].length=l;
edg[e].to=a;
}
void dfs(int i,int deep)
{
int j,max1=0;
poi[i].dep=deep;
poi[i].siz=1;
for (j=poi[i].first;j;j=edg[j].next)
{
if (poi[i].fa!=edg[j].to)
{
poi[edg[j].to].fa=i;
poi[edg[j].to].dis=poi[i].dis+edg[j].length;
dfs(edg[j].to,deep+1);
poi[i].siz+=poi[edg[j].to].siz;
if (poi[edg[j].to].siz>max1)
{
max1=poi[edg[j].to].siz;
poi[i].son=edg[j].to;
}
}
}
}
void dfs1(int i)
{
int j;
if (poi[poi[i].fa].son==i)
{
poi[i].top=poi[poi[i].fa].top;
}
else
poi[i].top=i;
for (j=poi[i].first;j;j=edg[j].next)
if (poi[i].fa!=edg[j].to)
dfs1(edg[j].to);
}
int lca(int a,int b)
{
while (poi[a].top!=poi[b].top)
{
if (poi[poi[a].top].dep<poi[poi[b].top].dep)
{
a=a^b;
b=a^b;
a=a^b;
}
a=poi[poi[a].top].fa;
}
return poi[a].dep<poi[b].dep?a:b;
}
int dfs2(int i)
{
int cnt=0,j;
for(j=poi[i].first;j;j=edg[j].next)
{
if (poi[i].fa!=edg[j].to)
{
edg[j].cnt=dfs2(edg[j].to);
poi[i].cnt+=poi[edg[j].to].cnt;
}
}
return poi[i].cnt;
}
bool check(int m,int mid)
{
int i,total=0,max1=0;
for (i=0;i<m;i++)
{
if (dis[i]>mid)
{
poi[u[i]].cnt++;
poi[v[i]].cnt++;
poi[LCA[i]].cnt-=2;
total++;
}
}
dfs2(1);
for (i=1;i<=2*n;i++)
if (edg[i].cnt==total)
if (edg[i].length>max1)
max1=edg[i].length;
return L-max1<=mid;
}
void main_main()
{
int i,left,right,mid;
memset(edg,0,2*N*sizeof(edge));
memset(poi,0,N*sizeof(point));
scanf("%d%d",&n,&m);
for (i=0;i<n-1;i++)
{
scanf("%d%d%d",&a[i],&b[i],&t[i]);
addedg(a[i],b[i],t[i]);
if (t[i]>max)
max=t[i];
}
for (i=0;i<m;i++)
scanf("%d%d",&u[i],&v[i]);
dfs(1,1);
dfs1(1);
for (i=0;i<m;i++)
{
dis[i]=poi[u[i]].dis+poi[v[i]].dis-2*poi[LCA[i]=lca(u[i],v[i])].dis;
if (L<dis[i])
L=dis[i];
}
left=(L-max)>0?L-max:0;
right=L;
while(left<right)
{
for(i=1;i<=n;i++)
poi[i].cnt=0;
for (i=1;i<=2*n;i++)
edg[i].cnt=0;
mid=(left+right)/2;
if (check(m,mid))
right=mid;
else
left=mid+1;
}
printf("%d",left);
}
const int main_stack=16;
char my_stack[128<<20];
int main()
{
__asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
__asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");
main_main();
__asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
return 0;
}