https://vjudge.net/contest/258556
B Connect3
题意:两人在4*4的棋盘里下棋,先手执黑第一步必下在(1,x),所有棋子只能下在第一行或者另一个棋子的上面。问:有多少种后手胜且最后一步下在(a,b)的方案。(不考虑下棋过程)
题解:因为条件严苛(轮流下,下棋位置少),所以合法情况不多,DFS和BFS都行,直接3进制判重即可。
#include<bits/stdc++.h>
using namespace std;
int Fr,EX,EY;
bool b[50000001];
int a[30][30];
int dl[11093001];
bool dll[11093001];
int Ans;
bool Win(int x,int y,int z)
{
a[x][y]=z;
for(int i=1;i<=4;i++)
{
if(a[i][1]==z&&a[i][2]==z&&a[i][3]==z) {a[x][y]=0;return true;}
if(a[i][4]==z&&a[i][2]==z&&a[i][3]==z) {a[x][y]=0;return true;}
if(a[1][i]==z&&a[2][i]==z&&a[3][i]==z) {a[x][y]=0;return true;}
if(a[4][i]==z&&a[2][i]==z&&a[3][i]==z) {a[x][y]=0;return true;}
}
for(int i=2;i<=3;i++)
for(int j=2;j<=3;j++)
{
if(a[i][j]==z&&a[i+1][j+1]==z&&a[i-1][j-1]==z) {a[x][y]=0;return true;}
if(a[i][j]==z&&a[i+1][j-1]==z&&a[i-1][j+1]==z) {a[x][y]=0;return true;}
}
a[x][y]=0;
return false;
}
int Rec()
{
int Sum=0;
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
Sum=Sum*3+a[i][j];
return Sum;
}
void Out(int x)
{
int X=x;
for(int i=4;i>=1;i--)
for(int j=4;j>=1;j--)
{
a[i][j]=X%3;
X/=3;
}
}
int main()
{
scanf("%d%d%d",&Fr,&EX,&EY);
a[1][Fr]=1;
int Now=Rec();
int t=0,w=1;
dl[1]=Now;
b[Now]=true;
dll[1]=true;
do
{
t++;
Out(dl[t]);
for(int j=1;j<=4;j++)
{
for(int i=1;i<=4;i++)
{
if(a[i][j]==0)
{
if(dll[t])
{
if(Win(i,j,2))
{
if(i==EX&&j==EY)
{
Ans++;
}
}
else
{
a[i][j]=2;
Now=Rec();
if(!b[Now]) b[Now]=true,dl[++w]=Now,dll[w]=false;
a[i][j]=0;
}
}
else
{
if(!Win(i,j,1))
{
a[i][j]=1;
Now=Rec();
if(!b[Now]) b[Now]=true,dl[++w]=Now,dll[w]=true;
a[i][j]=0;
}
}
break;
}
}
}
}while(t!=w);
printf("%d\n",Ans);
return 0;
}
C Game Map
题意:给出一张无向图,每次只能从度数少的点走向严格度数更多的点,求一条最长路径的长度。
题解:条件限制下其实是一张有向图,且没有环,最长路直接拓扑排序就行了。
#include<bits/stdc++.h>
#define MAXN 100000+1109
#define MAXM 300000+1109
using namespace std;
int n,m;
int DU[MAXN],Du[MAXN],RD[MAXN];
int L[MAXM],R[MAXM];
vector <int> f[MAXN];
int dl[MAXN];
int Ans[MAXN];
int main()
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
x++;y++;
L[i]=x;R[i]=y;
DU[x]++;DU[y]++;
}
for(int i=1;i<=m;i++)
{
x=L[i];y=R[i];
if(DU[x]<DU[y])
{
Du[x]++;RD[y]++;
f[x].push_back(y);
}
if(DU[y]<DU[x])
{
Du[y]++;RD[x]++;
f[y].push_back(x);
}
}
int t=0,w=0;
for(int i=1;i<=n;i++)
if(RD[i]==0)
dl[++w]=i,Ans[i]=1;
do
{
x=dl[++t];
for(int i=0;i<Du[x];i++)
{
y=f[x][i];
Ans[y]=max(Ans[y],Ans[x]+1);
RD[y]--;
if(RD[y]==0)
{
dl[++w]=y;
}
}
}while(t!=w);
int Re=-1;
for(int i=1;i<=n;i++)
/*printf("%d\n",Ans[i]),*/Re=max(Re,Ans[i]);
printf("%d",Re);
return 0;
}
D Happy Number
题意:定义函数f(n)为n各个位数上数字平方的和。例如f(19)=1^2+9^2=82。若反复应用f()函数后,一个数字变成了1,那么这是一个happy number,如果进入了循环,则是一个unhappy number,判断一个数字是什么种类的。
题解:经过一次f()后,数字会很小,直接上暴力就行了。
#include<cstdio>
#define MAXN 1109
using namespace std;
int n;
bool b[MAXN];
bool Find(int x)
{
if(x==1) return true;
if(b[x]) return false;
b[x]=true;
int X=x,Sum=0;
while(X)
{
Sum+=(X%10)*(X%10);
X/=10;
}
return Find(Sum);
}
int main()
{
scanf("%d",&n);
int x=0;
while(n)
{
x+=(n%10)*(n%10);
n/=10;
}
if(Find(x)) printf("HAPPY\n");
else printf("UNHAPPY\n");
return 0;
}
E How Many to Be Happy?
题意:给出一张无向图,对于一条边e,h(e)的函数值为至少去掉多少条边,使得e是一种最小生成树方案中的一边。求所有h(e)的和。
题解:n<=100,用网络流来做。最小生成树是从小到大加边,如果当前边不在一个联通块上,那么加入这条边。那么只要保证长度严格小于e的所有边不能构成树且边e可以联通两个块就行了。将所有长度严格小于e的边建立一个网络流模型,流量均为1,求一个e两个端点之间的最小割即可。这样就可以用最小的花费将e的两个端点割开,保证e能够在最小生成树时被加入。
#include<bits/stdc++.h>
#define MAXN 110
#define MAXM 500+30
using namespace std;
struct Data
{
int x,y,z;
}E[MAXM];
bool cmp(const Data &A,const Data &B)
{
return A.z<B.z;
}
int m,n,K,Du[MAXN],dis[MAXN],dl[2333333];
vector <int> f[MAXN],FB[MAXN],Flow[MAXN];
int cur[MAXN];
int Ans;
void ADD(int x,int y,int z)
{
Du[x]++;Du[y]++;
f[x].push_back(y);Flow[x].push_back(z);FB[x].push_back(Du[y]-1);
f[y].push_back(x);Flow[y].push_back(0);FB[y].push_back(Du[x]-1);
}
bool BFS(int Begin,int End)
{
int t=0,w=1,x,X;
memset(dis,0xff,sizeof(dis));
dis[Begin]=0;dl[1]=Begin;
do
{
x=dl[++t];
for(int i=0;i<Du[x];i++)
{
X=f[x][i];
if(Flow[x][i]<=0||dis[X]>=0) continue;//不连通或已访问
dis[X]=dis[x]+1;dl[++w]=X;
}
}while(t<w);
if(dis[End]>0) return true;
else return false;
}
int Find(int x,int MFLOW,int y)
{
if(x==y) return MFLOW;
int X,h;
for(int i=cur[x];i<Du[x];i++)
{
cur[x]=i;
X=f[x][i];
if(Flow[x][i]>0&&dis[x]+1==dis[X]&&(h=Find(X,min(MFLOW,Flow[x][i]),y)))
{
Flow[x][i]-=h;
Flow[X][FB[x][i]]+=h;
return h;
}
}
return 0;
}
int Solve(int x,int y)
{
int X,Ans=0;
while(1)
{
if(!BFS(x,y)) break;
memset(cur,0,sizeof(cur));
while((X=Find(x,0x7fffffff,y)))
Ans+=X;
}
return Ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].z);
sort(E+1,E+1+m,cmp);
for(int I=1;I<=m;I++)
{
for(int i=1;i<=n;i++)
{
Du[i]=0;
f[i].clear();FB[i].clear();Flow[i].clear();
}
for(int i=1;i<I;i++)
if(E[i].z<E[I].z)
ADD(E[i].x,E[i].y,1),ADD(E[i].y,E[i].x,1);
Ans+=Solve(E[I].x,E[I].y);
}
printf("%d",Ans);
return 0;
}
F Philosopher’s Walk
题意:给出一个哲学家在花园散步的路径规律,这个规律比较符合分形的思想。给出这个花园的规模和哲学家走了多久,求出这个哲学家当前的坐标。
题解:递归求解即可,具体从题面中提取的操作见代码。
#include<bits/stdc++.h>
using namespace std;
int n,K;
int X,Y;
int L[30]={0,0,1,1};
int R[30]={0,1,1,0};
void Find(int x,int y)
{
if(x==1) return ;
int Num=(y-1)/(x*x/4);
X+=L[Num]*(x/2);
Y+=R[Num]*(x/2);
if(Num==0) swap(L[1],L[3]),swap(R[1],R[3]);
if(Num==3) swap(L[0],L[2]),swap(R[0],R[2]);
Find(x/2,y-Num*(x*x/4));
}
int main()
{
scanf("%d%d",&n,&K);
Find(n,K);
printf("%d %d",X+1,Y+1);
return 0;
}
I Slot Machines
题意:给出一个数字序列,除去前k个数字后,剩下的数列是一个循环节长度为p的循环序列。有多个k和p都满足这个序列,求最小的k+p,若有相同,则取最小的p。
题解:反向KMP。将数列倒过来,枚举到一位数字时,认为这数字后面的是那前k位,后面的循环节长度就是i-next[i]。
#include<bits/stdc++.h>
#define MAXN 1000000+1109
using namespace std;
int K,P,Ans=0x7fffffff;
int plen;
int p[MAXN];
int f[MAXN];//失配函数
void getFail() {
//int plen = p.length();
f[0] = 0; f[1] = 0;
for (int i = 1; i < plen; i++) {
int j = f[i];
while (j && p[i] != p[j]) j = f[j];
f[i + 1] = (p[i] == p[j]) ? j + 1 : 0;
}
}
int main()
{
scanf("%d",&plen);
for(int i=plen-1;i>=0;i--)
scanf("%d",&p[i]);
getFail();
int k,p;
for(int i=1;i<=plen;i++)
{
k=i-f[i];
p=plen-i;
if(k+p>Ans) continue;
if(k+p==Ans)
{
if(k>=K) continue;
K=k;P=p;
}
else
{
Ans=k+p;
K=k;P=p;
}
}
printf("%d %d",P,K);
return 0;
}