電子科技大學第九屆ACM趣味程式設計競賽第二場(正式賽) 題解
A::魔王的直播
注意23:59之後是00:00即可,找出所有迴文字元,取最近.
#include <bits/stdc++.h> using namespace std; bool check(int a,int b) { int c=a%10*10+a/10; if(c==b) return false; return true; } int main() { int a,b; scanf("%d:%d",&a,&b); int ans=0; while(check(a,b)) { ans++; b++; a+=b/60; b%=60; a%=24; } cout<<ans<<endl; }
B:綠帽自動機
題意:
定義“戴綠帽”操作:除了制定某一個某一個人之外,其他人的綠帽數+1。
給定n個數,進行儘量少的“戴綠帽”操作,使得第x個數最大。
輸出“戴綠帽”操作所需的最少次數。
首先,明確一點,如果我一次性給所有人“戴綠帽”,那麼“戴綠帽”前後,最大的那個人的綠帽數依舊最大,最小的那個依舊最小,即不影響題目要求。
現在題意為“ 除了指定的某一個人以外,其餘所有人綠帽數+1”,可以分解為以下操作:
1.給所有人綠帽數+1
2.給指定的那個人綠帽數-1
事實上,題目就等價於:每次減少一個人的綠帽,至少進行多少次減少操作,可以使得第x-th數最大。
假設第x人原綠帽數為g,那麼計算一下所有大於等於g-1的數與g-1的差即可。
結果可能爆int,請使用long long。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 100000+10; ll g[MAXN]; ll sum; int main() { int n,x; cin>>n>>x; for(int i=1;i<=n;++i) cin>>g[i]; for(int i=1;i<=n;++i) if(g[i]>=g[x]-1) sum+=(g[i]-g[x]+1); cout<<sum-1; return 0; }
C:洗牌機
洗牌方式是一個大環,因此連續洗牌 13 次後所有牌又會回到原位,那麼洗一次牌相當於洗7*2次牌。
我們只需要模擬洗7*2次即可。
根據初始順序和洗兩次後的順序,得到洗兩次的洗牌方式,用二重迴圈,外迴圈模擬每一次洗牌,內迴圈模擬洗每一張牌,再輸出洗7*2次牌後的結果。
#include <bits/stdc++.h>
using namespace std;
#define N 15
int a[N][N],nxt2[N],num;
//a[i][j]��ʾϴi���ƺ�ĵ�j����
//nxt2[i]=j��ʾԭ����i������ϴ���κ��ܵ���j��
int main()
{
int n=13,times=7;
scanf("%d",&num);
while(num--)
{
for(int i=1; i<=n; i++)
scanf("%d",&a[0][i]);
for(int i=1; i<=n; i++)
scanf("%d",&a[2][i]);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(a[0][i]==a[2][j])
{
nxt2[i]=j;
break;
}
for(int t=1; t<times; t++)
for(int i=1; i<=n; i++)
{
a[2*(t+1)][nxt2[i]]=a[2*t][i];
}
for(int i=1; i<=n; i++)
{
printf("%d ",a[14][i]);
}
printf("\n");
}
return 0;
}
D:小帆寶與數字8
題目要求你得到一個長度為n的排列,使得相鄰兩個數的乘積都是8的倍數
1 2 4 8,這四個數,可以知道,1和1不能在一起,1和2不能在一起,1和4不能再一起,2和2不能在一起,其他都可以在相鄰
並且1只能和8在一起,所以要讓1旁邊有儘量少的空位,那麼我們就從第一個位置開始放1,並且隔一個位置,再放下一個1,直到把1放完,這樣使得1旁邊
有最少的空位。
如果這樣放到最後要給位置,還沒有把1放完,那麼就是無解,輸出NO
對於2,只要和2不相鄰且和1不相鄰。所以接著之前放1的位置的下下個位置放2,直到把2放完,這樣可以放置最多的2。
如果這樣放到最後一個位置,還沒有把2放完,那麼就是無解,輸出NO
然後在空著的位置,先放8,再放4
最後check一下,相鄰的乘積是否為8的倍數
如果不是,輸出NO
如果是,輸出YES
舉個例子
6
1 1 2 4 8 8
先放置1,即第一個和第三個位置為1
再放置2,即第五個位置為2
然後先放8,即第二和第四個位置是8
最後放置4,即第六個位置是4
1 8 1 8 2 4 答案即是這個。
#include <iostream>
#include <cstdio>
using namespace std;
int a[4];
int b[105];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
if(x==1) a[0]++;
if(x==2) a[1]++;
if(x==4) a[2]++;
if(x==8) a[3]++;
}
int flag=0;
for(int i=0;i<n;i+=2)
{
if(a[flag]==0)
{
if(flag==0) flag++;
if(a[flag]==0) break;
}
b[i]=flag+1;
a[flag]--;
}
if(a[0]!=0||a[1]!=0)
{
printf("NO\n");
return 0;
}
flag=3;
for(int i=0;i<n;i++)
{
if(b[i]!=0) continue;
if(a[flag]==0)
{
if(flag==3) flag--;
}
if(flag==3) b[i]=8;
else b[i]=4;
a[flag]--;
}
bool sign=0;
for(int i=1;i<n;i++)
{
if(b[i]*b[i-1]%8!=0)
{
printf("NO\n");
sign=1;
}
}
if(sign==0)
{
printf("YES\n");
for(int i=0;i<n;i++) printf("%d ",b[i]);
}
return 0;
}
E:矩陣
當K=1時
先隨便寫出一個(N-1)*(M-1)的元素為1或-1的矩陣
對第i(1<=i<=N-1)行進行分類討論
如果第i行裡第1列到M-1列的元素之積為1,則在第N行第M列裡填充1,則顯然會使得第i行從第1列到第M列的元素之積為1,
如果第i行裡第1列到M-1列的元素之積為-1,則在第N行第M列裡填充-1,則顯然會使得第i行從第1列到第M列的元素之積也為1,
也就是說令
對第j(1<=J<=M-1)列也這樣處理,即令
這樣分別處理完以後就可以保證第1行到第N-1行的各行內元素之積一定為1且第1行到第N-1列的各列內元素之積也一定為1,那麼只要使得第N行和第M列的元素之積為1就可以了。
因為矩陣A的元素 ,
那麼可以得到和,容易得到,也就是說不論在第N行第M列裡填充1或者-1,都會使得第N行和第M列內各元素之積相等,所以,只要令就可以使得這個矩陣滿足條件了。而且,顯然可以的知道不論一個什麼樣的(N-1)*(M-1)的矩陣都有且只有一個滿足K=1的條件的N*M的矩陣與之對應,也就是說當K=1時,答案就是
當K=-1的時候,方法與k=1的時候類似,但是當N和M的奇偶性不一樣的時候就會使得不論一個什麼樣的(N-1)*(M-1)的矩陣都不會存在一個與之對應的符合條件的N*M的矩陣。因為此時 ,而
不論在第N行第M列裡填充1或者-1,都會使得第N行和第M列內各元素之積都不 相等。
所以當K=-1且N,M的奇偶性相同時,答案就是,否則是0。
#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
using namespace std;
int n , m , k;
int main(){
scanf("%d%d%d",&n,&m,&k);
if ( ( k == 1) || ( ( k==-1 ) && ( (n+m)%2== 0 ) ) )
{
long long ans=1;
for (int i = 1;i<= n-1 ;i++)
for (int j= 1; j<=m-1;j++)
ans=ans*2ll;
cout<<ans<<endl;
}
else
puts("0");
return 0;
}