T1
神奇的幻方
水題,模擬就行。但是初賽可能會考幻方補全程序.
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 40*40;
int row[MAXN],col[MAXN];
int res[40][40];
int n;
int main(){
scanf("%d",&n);
res[1][(n+1)/2] = 1;
row[1] = 1;col[1] = (n+1)/2;
for(int i = 2;i<=n*n;i++){
if(row[i-1]==1&&col[i-1]!=n){
//若 (K-1) 在第一行但不在最後一列,則將 K 填在最後一行, (K-1) 所在列的右一列
row[i] = n,col[i] = col[i-1]+1;
res[row[i]][col[i]] = i;
}else if(col[i-1]==n&&row[i-1]!=1){
//若(K-1)在最後一列但不在第一行,則將K填在第一列,(K-1)所在行的上一行;
col[i] = 1;row[i] = row[i-1]-1;
res[row[i]][col[i]] = i;
}else if(row[i-1]==1&&col[i-1]==n){
//若(K-1)在第一行最後一列,則將K填在(K-1)的正下方;
row[i] = row[i-1]+1;col[i] = col[i-1];
res[row[i]][col[i]] = i;
}else{
if(res[row[i-1]-1][col[i-1]+1]==0){
row[i] = row[i-1]-1;col[i] = col[i-1]+1;
res[row[i]][col[i]] = i;
}else{
row[i] = row[i-1]+1;col[i] = col[i-1];
res[row[i]][col[i]] = i;
}
}
}
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
printf("%d ",res[i][j]);
}
puts("");
}
return 0;
}
T2
信息傳遞
就是求最小環
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
#define M 200005
#define inf (1ll<<31ll)-1
using namespace std;
int t,cnt,sum,top,ans=inf;
int low[N],dfn[N],sta[N];
int first[N],v[M],next[M];
bool insta[N];
void add(int x,int y)
{
t++;
next[t]=first[x];
first[x]=t;
v[t]=y;
}
void Tarjan(int x)
{
int i,k;
cnt++;
low[x]=cnt;
dfn[x]=cnt;
top++;
sta[top]=x;
insta[x]=true;
k=first[x];
for(i=first[x];i;i=next[i])
{
k=v[i];
if(!dfn[k])
{
Tarjan(k);
low[x]=min(low[x],low[k]);
}
else if(insta[k])
low[x]=min(low[x],dfn[k]);
}
if(low[x]==dfn[x])
{
int sum=0;
do
{
sum++;
i=sta[top--];
insta[i]=false;
}
while(i!=x);
if(sum!=1)
ans=min(ans,sum);
}
}
int main()
{
int n,i,x;
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d",&x);
add(i,x);
}
for(i=1;i<=n;++i)
if(!dfn[i])
Tarjan(i);
printf("%d",ans);
return 0;
}
T3
鬥地主
模擬題,策略就是先找順子,再找帶的,最後出單牌。反正這題是因爲數據水,這種策略其實是錯誤的。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 16;
int card[MAXN];
void add(int v){
if(v>=3&&v<=10)card[v-2]++;
if(v==1)card[12]++;
if(v==11)card[9]++;
if(v==12)card[10]++;
if(v==13)card[11]++;
if(v==0)card[14]++;
if(v==2)card[13]++;
}
int ans;
void dfs(int x){
if(x>=ans)return;
int k = 0;
for(int i = 1;i<=12;i++){//順子
if(!card[i])k = 0;
else{
k++;
if(k>=5){
for(int j = i;j>=i-k+1;j--)card[j]--;
dfs(x+1);
for(int j = i;j>=i-k+1;j--)card[j]++;
}
}
}
k = 0;
for(int i = 1;i<=12;i++){//雙順子
if(card[i]<=1)k = 0;
else{
k++;
if(k>=3){
for(int j = i;j>=i-k+1;j--)card[j]-=2;
dfs(x+1);
for(int j = i;j>=i-k+1;j--)card[j]+=2;
}
}
}
k = 0;
for(int i = 1;i<=12;i++){//三順子
if(card[i]<=2)k = 0;
else{
k++;
if(k>=2){
for(int j = i;j>=i-k+1;j--)card[j]-=3;
dfs(x+1);
for(int j = i;j>=i-k+1;j--)card[j]+=3;
k = 0;
}
}
}
for(int i = 1;i<=13;i++){
if(card[i]==3){
card[i]-=3;
for(int j = 1;j<=14;j++){//三帶一
if(card[j]&&j!=i){
card[j]--;
dfs(x+1);
card[j]++;
}
}
for(int j = 1;j<=13;j++){//三代一對
if(card[j]>=2&&i!=j){
card[j]-=2;
dfs(x+1);
card[j]+=2;
}
}
card[i]+=3;
}else if(card[i]==4){
card[i]-=3;
for(int j = 1;j<=14;j++){//三帶一
if(card[j]&&j!=i){
card[j]--;
dfs(x+1);
card[j]++;
}
}
for(int j = 1;j<=13;j++){//三代一對
if(card[j]>=2&&i!=j){
card[j]-=2;
dfs(x+1);
card[j]+=2;
}
}
card[i]+=3;
card[i]-=4;
for(int j = 1;j<=14;j++){//四帶一
if(card[j]>0&&j!=i){
for(int k = 1;k<=14;k++){
if(j==k&&j==14&&card[14]==2){//四帶大小王
card[14]-=2;
dfs(x+1);
card[14]+=2;
}else if(j!=k&&i!=k&&card[k]>0){
card[j]--;card[k]--;
dfs(x+1);
card[j]++;card[k]++;
}
}
}
}
for(int j = 1;j<=13;j++){
if(j!=i&&card[j]>=2)
for(int k = 1;k<=13;k++){
if(j!=k&&i!=k&&card[k]>=2){
card[j]-=2;card[k]-=2;
dfs(x+1);
card[j]+=2;card[k]+=2;
}
}
}
card[i]+=4;
}
}
for(int i = 1;i<=14;i++)if(card[i])x++;
ans = min(ans,x);
}
int main(){
int T,n;
scanf("%d %d",&T,&n);
while(T--){
memset(card,0,sizeof(card));
int x,y;
ans = n;
for(int i = 1;i<=n;i++){
scanf("%d %d",&x,&y);
add(x);
}
dfs(0);
printf("%d\n",ans);
}
return 0;
}
T4
跳石頭
二分板子題
#include<cstdio>
int read(){
int flag = 1,n = 0;
char ch;
ch = getchar();
while(ch>'9'||ch<'0'){
if(ch=='-')flag = -1;
ch = getchar();
}
while(ch>='0'&&ch<='9'){
n*=10;
n+=ch-'0';
ch = getchar();
}
return n*flag;
}
int L,M,N;
int stones[50010];
bool check(int l){
int tot = 0;
int last = 0;
for(int i = 1;i<=N;i++){
if(stones[i]-last<l){
tot++;
}else{
last = stones[i];
}
if(tot>M)return false;
}
if(L-last<l)tot++;
return tot<=M;
}
int main(void){
L = read();N = read();M = read();
for(int i = 1;i<=N;i++){
stones[i] = read();
}
int l = 0;int r = L;
while(l<r){
int mid = (l+r+1)>>1;
if(check(mid)){
l = mid;
}else{
r = mid-1;
}
}
printf("%d",l);
return 0;
}
T5
子串
顯然是個DP。dp[i][j][k][0/1]表示A匹配i位B匹配j位分成k段,第i位選不選。
則有如下轉移
若A[i]=B[j]
否則
代碼:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 1010;
const int MAXM = 210;
const int mod = 1000000007;
int dp[2][MAXM][MAXM][2];
char A[MAXN],B[MAXN];
int n,m,kk;
void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
int main(){
scanf("%d%d%d",&n,&m,&kk);
scanf("%s %s",A+1,B+1);
dp[1][0][0][0] = dp[0][0][0][0] = 1;
for(register int i = 1;i<=n;i++){
for(register int j = 1;j<=m;j++){
for(register int k = 1;k<=kk;k++){
if(A[i]==B[j]){
dp[i&1][j][k][0] = (dp[(i-1)&1][j][k][0]+dp[(i-1)&1][j][k][1])%mod;
dp[i&1][j][k][1] = (dp[(i-1)&1][j-1][k][1]+(dp[(i-1)&1][j-1][k-1][1]+dp[(i-1)&1][j-1][k-1][0])%mod)%mod;
}else{
dp[i&1][j][k][0] = (dp[(i-1)&1][j][k][0]+dp[(i-1)&1][j][k][1])%mod;
dp[i&1][j][k][1] = 0;
}
}
}
}
write((dp[n&1][m][kk][0]+dp[n&1][m][kk][1])%mod);
return 0;
}
T6
運輸計劃
可以一眼看出是個二分答案。然後統計出長度大於mid的路徑的條數。考慮如何統計一條邊被多少條路徑經過,考慮樹上差分。sum[x]++,sum[y]++,sum[lca]-=2;即可從底到頂統計出來。然後找到一條長度大於mid的,被超過num條路徑經過的邊即可。
代碼:
#include<cstdio>
#include<cstring>
#define M 300001
#include<algorithm>
using namespace std;
struct nn{
int to,next,val;
};
nn ed[M<<1];
struct nnn {
int a,b,ans,dist;
};
nnn lca[M];
int head[M],tot,b[M];
int n,m,ans,l,r;
int f[M][31],dep[M],dis[M],sum[M];
inline int read() {
int x=0;char c=getchar();
while(c>'9'||c<'0') c=getchar();
while(c>='0'&&c<='9') x=10*x+c-48,c=getchar();
return x;
}
inline void add(int x,int y,int z) {
ed[++tot].to=y;
ed[tot].val=z;
ed[tot].next=head[x];
head[x]=tot;
}
inline void dfs(int x,int from,int de,int l) {
dep[x]=de;
dis[x]=l;
f[x][0]=from;
for(int i=head[x];i;i=ed[i].next) {
int v=ed[i].to;
if(v!=from) {
b[v]=i;
dfs(v,x,de+1,l+ed[i].val);
}
}
}
inline int LCA(int a,int b) {
if(dep[a]<dep[b]) swap(a,b);
int t=dep[a]-dep[b];
for(int i=0;i<=30;i++)
if((1<<i)&t) a=f[a][i];
if(a==b) return a;
for(int i=30;i>=0;i--)
if(f[a][i]!=f[b][i])
a=f[a][i],b=f[b][i];
return f[a][0];
}
inline void updata(int now,int from) {
for(int i=head[now];i;i=ed[i].next) {
int v=ed[i].to;
if(v!=from) {
updata(v,now);
sum[now]+=sum[v];
}
}
}
inline bool check(int x) {
int cnt=0,dec=0;
memset(sum,0,sizeof(sum));
for(int i=1;i<=m;i++)
if(lca[i].dist>x) {
cnt++;
sum[lca[i].a]++;
sum[lca[i].b]++;
sum[lca[i].ans]-=2;
dec=max(dec,lca[i].dist-x);
}
updata(1,1);
for(int i=1;i<=n;i++) if(sum[i]==cnt&&ed[b[i]].val>=dec) return true;
return false;
}
int main() {
n=read();m=read();
for(int i=1;i<n;i++) {
int x,y,z;
x=read();y=read();z=read();
add(x,y,z);add(y,x,z);
}
dfs(1,1,0,0);
for(int j=1;j<=30;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
for(int i=1;i<=m;i++) {
lca[i].a=read();
lca[i].b=read();
lca[i].ans=LCA(lca[i].a,lca[i].b);
lca[i].dist=dis[lca[i].a]+dis[lca[i].b]-(dis[lca[i].ans]<<1);
r=max(r,lca[i].dist);
}
r++;
while(l<r) {
int mid=(l+r)>>1;
if(check(mid)) ans=r=mid;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}