# 演算法課程題解 - 暴力與窮舉 1
# UVa 100
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?100
## 解法 By Koios1143
### 想法
因為無法透過一個公式或有效率的方式得到每個數字的 cycle length,因此直接暴力跑過,計算每個數值的 cycle length,再留下最大值即可。
需要注意的是,題目中並沒有保證 $i$ $j$ 的大小關係。
### AC code
```cpp=
// By Koios1143
#include<iostream>
using namespace std;
int f(int n){
if(n == 1)
return 1;
if(n%2 == 1)
return f(3*n+1)+1;
else
return f(n/2)+1;
}
int main(){
int i, j;
while(cin>>i>>j){
cout<<i<<" "<<j<<" ";
if(i > j)
swap(i, j);
int ans=0;
for(int k=i ; k<=j ; k++){
ans = max(ans, f(k));
}
cout<<ans<<"\n";
}
return 0;
}
```
# UVa 12289
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?12289
## 解法 by tim25871014
### 想法
形如`on?`、`o?e`、`?ne`的都是1,五個字母的是3,剩下都是2。
### AC code
```cpp=1
#include <iostream>
using namespace std;
int main() {
int n;
string s;
cin >> n;
for(int i=0;i<n;i++) {
cin >> in;
int check = 2;
if(s[0] == 'o' && s[1] == 'n') check = 1;
if(s[0] == 'o' && s[2] == 'e') check = 1;
if(s[1] == 'n' && s[2] == 'e') check = 1;
if(s.size() == 5) check = 3;
cout << check << endl;
}
}
```
# UVa 10377
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?10377
## 解法 By Koios1143
### 想法
跟著給定的操作走即可。
為了方便處理方向,我把 北(N)、東(E)、南(S)、西(W) 分別以 0、1、2、3 表示。
如此一來當我要向右轉,就只需要把方向加上 $1$,然後再模 $4$。
> 可以想成,$3$ 加上 $1$ 之後希望變成 $0$,所以需要再扣掉 $4$
> 用 mod 的方式也是相同
反之,如果要向左轉,就只需要把方向減去 $1$,當數字小於 $0$,就要回到 $3$,因此加上 $4$。
至於前進時要怎麼處理 $x, y$ 座標,我們可以製作兩個陣列,分別表示 $x, y$ 的變移量
我習慣上喜歡把列(row)當成 $x$ 軸,把行(column)當成 $y$ 軸,所以我可以整理出在面向不同方向時變化量分別是:
> $x$: $-1$, $0$, $1$, $0$
> $y$: $0$, $1$, $0$, $-1$
### AC Code
```cpp=
// By Koios1143
#include<iostream>
using namespace std;
string s;
int T, N, M, x, y, dir;
char op, arr[10000][10000];
char face[] = {'N', 'E', 'S', 'W'};
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int main(){
bool out = false;
cin>>T;
while(T--){
if(out)
cout<<"\n";
else
out = true;
cin>>N>>M;
getchar(); // To avoid getline receive new line (RF)
for(int i=1 ; i<=N ; i++){
getline(cin, s);
for(int j=1 ; j<=M ; j++){
arr[i][j] = s[j-1];
}
}
cin>>x>>y;
dir = 0;
while(cin>>op && op != 'Q'){
if(op == 'R'){
dir = (dir + 1)%4;
}
else if(op == 'L'){
dir--;
if(dir < 0)
dir += 4;
}
else if(op == 'F'){
int nx = x + dx[dir];
int ny = y + dy[dir];
if(arr[nx][ny] == '*' || nx <= 0 || ny <= 0 || nx > N || ny > M)
continue;
x = nx;
y = ny;
}
}
cout<<x<<" "<<y<<" "<<face[dir]<<"\n";
}
return 0;
}
```
# UVa 608
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?608
## 解法 By Koios1143
### 想法
我沒有想到什麼很有效率的判斷方式,不過既然硬幣的數量很少,也知道必定只有一個假硬幣,也就是答案是唯一的,那麼可以直接假設每個硬幣是假硬幣的情況下,是否能符合每次秤重的結果即可。
### AC Code
```cpp=
// By Koios1143
#include<iostream>
using namespace std;
int T, coins[20];
string LeftItem[3], RightItem[3], Res[3];
bool check(){
bool ret = true;
for(int i=0 ; i<3 ; i++){
int L=0, R=0;
for(int j=0 ; j<LeftItem[i].size() ; j++){
L += coins[LeftItem[i][j]-'A'];
}
for(int j=0 ; j<RightItem[i].size() ; j++){
R += coins[RightItem[i][j]-'A'];
}
if((L == R && Res[i] == "even") || (L > R && Res[i] == "up") || (L < R && Res[i] == "down"))
ret &= true;
else
ret &= false;
}
return ret;
}
int main(){
cin>>T;
while(T--){
bool found = false;
for(int i=0 ; i<3 ; i++){
cin>>LeftItem[i]>>RightItem[i]>>Res[i];
}
for(int fake=0 ; fake<12 ; fake++){ // 枚舉誰是假幣的情況
for(int w=-1 ; w<=1 && !found ; w+=2){ // 枚舉假幣是輕或重
//init
for(int i=0 ; i<12 ; i++){
coins[i] = 0;
}
coins[fake] = w;
if(check()){
found = true;
if(w == -1)
cout<<(char)('A'+fake)<<" is the counterfeit coin and it is light.\n";
else
cout<<(char)('A'+fake)<<" is the counterfeit coin and it is heavy.\n";
}
}
}
}
return 0;
}
```
# UVa 101
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?101
## 解法 by baie
### 想法
題目要求的四個動作可以歸納成兩種,分別是放到別堆和放回原本的位置,所以用sb實作放回原本的位置,用sb1放到別堆,再分別依照四種要求作細部修改
### 程式碼
```cpp=
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define pob pop_back
int n,b[30];
int x,y,w,e,k;
vector<int> a[30];
void sb(int x)
{
for(int i=0;i<a[b[x]].size();i++)
if(a[b[x]][i]==x) w = i+1;
while(!a[b[x]].empty()&&a[b[x]].size()>w)
{
k = a[b[x]].back();
a[b[x]].pob();
a[k].pb(k);
b[k] = k;
}
}
void sb1(int x)
{
for(int i=0;i<a[b[x]].size();i++)
if(a[b[x]][i]==x) w = i;
a[b[y]].pb(a[b[x]][w]);
for(int i=w+1;i<a[b[x]].size();i++)
a[b[y]].pb(a[b[x]][i]),b[a[b[x]][i]] = b[y];
while(!a[b[x]].empty()&&a[b[x]].size()>w)
a[b[x]].pob();
}
int main()
{
string s,s1;
while(cin>>n)
{
for(int i=0;i<n;i++) a[i].clear();
for(int i=0;i<n;i++) a[i].pb(i),b[i]=i;
while(cin>>s)
{
if(s=="quit") break;
else cin>>x>>s1>>y;
if(b[x]==b[y]) continue;
w=0,e=0;
if(s=="move"&&s1=="onto")
{
sb(x);
sb(y);
a[b[y]].pb(a[b[x]].back()),a[b[x]].pob();
}
else if(s=="move"&&s1=="over")
{
sb(x);
a[b[y]].pb(a[b[x]].back()),a[b[x]].pob();
}
else if(s=="pile"&&s1=="onto") sb(y),sb1(x);
else if(s=="pile"&&s1=="over") sb1(x);
b[x] = b[y];
}
for(int i=0;i<n;i++)
{
cout<<i<<":";
for(int j=0;j<a[i].size();j++) cout<<" "<<a[i][j];
cout<<endl;
}
}
return 0;
}
```
### 複雜度分析
m為指令次數,時間複雜度為 $O(nm)$
# UVa 102
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?102
## 解法 by baie
### 想法
因答案需求如果搬移量一樣的話,以最小字典序輸出,且桶子只有三個。所以可以直接建好字典序表,直接窮舉每一個不同字典序的搬移量,最小最前的就是答案
### 程式碼
```cpp=
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[3][3];
bool b[3][3];
int w[6][3] = {{0,2,1},{0,1,2},{2,0,1},{2,1,0},{1,0,2},{1,2,0}};
while(cin>>a[0][0])
{
cin>>a[0][1]>>a[0][2];
for(int i=1;i<3;i++)
for(int j=0;j<3;j++)
cin>>a[i][j];
memset(b,0,sizeof(b));
int x,y,z,mi = 2147483647;
for(int k=0,sum=0;k<6;sum=0,k++)
{
b[0][w[k][0]] = 1, b[1][w[k][1]] = 1, b[2][w[k][2]] = 1;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(!b[i][j])
sum += a[i][j];
else
b[i][j] = 0;
}
}
if(sum < mi)
x = w[k][0], y = w[k][1], z = w[k][2] , mi = sum;
}
char q[3] = {'B','G','C'};
cout<<q[x]<<q[y]<<q[z]<<" "<<mi<<endl;
}
return 0;
}
```
### 複雜度分析
時間複雜度為$O(1)$
# UVa 573
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?573
## 解法 by baie
### 想法
蝸牛一天的順序是爬->滑->變累,只要依照題目要求實作(注意變累是取締一天爬的長度的10%),因結果只會有出井和滑回井底兩種,故break的條件如下:
1. 爬出井(要在滑之前判斷)
2. 滑回井底
如果達成條件則輸出狀況和目前天數,即為答案
### 程式碼
```cpp=
#include<bits/stdc++.h>
using namespace std;
int main()
{
double h,u,d,f;
while(cin>>h>>u>>d>>f&&h!=0)
{
double sum = 0,e = (u * f) / 100;
int w = 1;
while(true)
{
if(w>1) u -= e;
if(u<0) u = 0;
sum += u;
if(sum>h)
{
cout<<"success on day "<<w<<endl;
break;
}
sum -= d;
if(sum<0)
{
cout<<"failure on day "<<w<<endl;
break;
}
w++;
}
}
return 0;
}
```
### 複雜度分析
時間複雜度即為爬的天數w $O(w)$
# UVa 10310
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?10310
## 解法 By Koios1143
### 想法
針對每一個洞,分別去計算到田鼠和狗之間的距離,判斷田鼠是否可以逃走。
需要注意的是,如果田鼠和狗同時到達洞,算是成功逃走的。
### AC Code
```cpp=
// By Koios1143
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
int n;
double Gx, Gy, Dx, Dy, Hx, Hy, Rx, Ry;
double dis(double Ax, double Ay, double Bx, double By){
return (pow(abs(Ax-Bx), 2) + pow(abs(Ay-By), 2));
}
int main(){
while(cin>>n>>Gx>>Gy>>Dx>>Dy){
bool escape=false;
for(int i=0 ; i<n ; i++){
cin>>Hx>>Hy;
if(!escape && (dis(Hx, Hy, Gx, Gy) <= dis(Hx, Hy, Dx, Dy)/4.0)){
escape = true;
Rx = Hx;
Ry = Hy;
}
}
if(escape)
cout<<fixed<<setprecision(3)<<"The gopher can escape through the hole at ("<<Rx<<","<<Ry<<").\n";
else
cout<<"The gopher cannot escape.\n";
}
return 0;
}
```
###### tags: `SCIST 演算法 題解`