https://programmers.co.kr/learn/courses/30/lessons/42584

 

코딩테스트 연습 - 주식가격

초 단위로 기록된 주식가격이 담긴 배열 prices가 매개변수로 주어질 때, 가격이 떨어지지 않은 기간은 몇 초인지를 return 하도록 solution 함수를 완성하세요. 제한사항 prices의 각 가격은 1 이상 10,00

programmers.co.kr

문제를 처음 확인할 때 생각해볼 수 있는 가장 쉬운 방법으로는 O(n^2) 시간 복잡도를 가지고 있는 완전 탐색이다.

 

그러나 문제를 읽어보면 다음 인덱스 시점의 시간에서 가격이 떨어지느냐 안 떨어지느냐에 따라 return값이 결정된다는 것을 확인할 수 있다.

 

한 가지 주의해야 할 점은 다음 인덱스가 이전 인덱스보다 작다는 것을 알기 전 시간은 이미 1초가 흘렀다고 생각하는 것이고

 

전부 다 돌아보는 것이 아닌 두 번째 for문부터는 바로 앞 인덱스부터 탐색을 시작하면 해결할 수 있다.

 

문제의 분류로는 스택과 큐 카테고리에 존재하지만 오히려 스택과 큐를 활용하여 해결방법을 생각해보더라도 오히려 코드가 더 길어질 것 같으며 큰 차이가 없을 것 같아 다음과 같이 완전 탐색과 같은 방법으로 해결하였다.

 

3 언어의 코드 방식도 전부 동일하며 문제 예시에 대한 이해를 빠르게 한다면 빠른 시간 내에 해결할 수 있을 것 같다.

 

C++

#include <string>
#include <vector>

using namespace std;

vector<int> solution(vector<int> prices) 
{
    vector<int> answer;
    for(int i=0;i<prices.size();i++)
    {
        int cnt=0;
        for(int j=i+1;j<prices.size();j++)
        {
            cnt++;
            if (prices[i] > prices[j])
                break;
        }
        answer.push_back(cnt);
    }
    return answer;
}

Java

class Solution 
{
    public int[] solution(int[] prices) 
    {
        int[] answer = new int[prices.length];
        for(int i=0;i<prices.length;i++)
        {
            for (int j=i + 1;j<prices.length;j++)
            {
                answer[i]++;
                if (prices[i]>prices[j])
                    break;
            }
        }
        return answer;
    }
}

Python3

def solution(prices):
    answer = [0] * len(prices)
    # prices 크기만큼 빈 리스트를 할당.
    for i in range(0,len(prices)):
        for j in range(i + 1, len(prices)):
            answer[i] += 1
            if prices[i]>prices[j]:
                break;
    return answer

 

https://github.com/ukjinlee66/BOJ/blob/master/17406.cpp

 

ukjinlee66/BOJ

baekjoon Online Judge problem. Contribute to ukjinlee66/BOJ development by creating an account on GitHub.

github.com

https://www.acmicpc.net/problem/17406

 

17406번: 배열 돌리기 4

크기가 N×M 크기인 배열 A가 있을때, 배열 A의 값은 각 행에 있는 모든 수의 합 중 최솟값을 의미한다. 배열 A가 아래와 같은 경우 1행의 합은 6, 2행의 합은 4, 3행의 합은 15이다. 따라서, 배열 A의 값은 4이다. 1 2 3 2 1 1 4 5 6 배열은 회전 연산을 수행할 수 있다. 회전 연산은 세 정수 (r, c, s)로 이루어져 있고, 가장 왼쪽 윗 칸이 (r-s, c-s), 가장 오른쪽 아랫 칸이 (r+s, c+s)인 정사각형을 시계

www.acmicpc.net

문제에서 시키는 방법대로만 구현하면 되는 문제였다. 다만 배열을 돌리기 위해서 가장 바깥 단계부터 안쪽으로 몇 단계까지 돌려야 하는지는 s라는 변수를 생각하여 코딩하였고 3번의 시도 끝에 풀게 되었는데 next_permutaion 사용 시 vector라던지 배열이 sort 되어있는 상태에서 사용되어야 한다는 것을 다시 한번 생각하는 시간이 되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<intint> P;
vector<pair<intpair<intint>>> turn;
int map[51][51];
int copmap[51][51];
int n, m, k;
int res = 987654321;
void print_map()
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
            cout << map[i][j] << " ";
        puts("");
    }
    puts("");
}
void recover_map()
{
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            map[i][j] = copmap[i][j];
}
void copy_map()
{
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            copmap[i][j] = map[i][j];
}
void check_minnum_sum()
{
    for (int i = 0; i < n; i++)
    {
        int num = 0;
        for (int j = 0; j < m; j++)
        {
            num += map[i][j];
        }
        res = min(res, num);
    }
}
void turning(int r, int c, int s)
{
    while (s!=0
    {
        int temp = map[r - s][c - s];
        for (int i = r - s; i < r + s; i++)
            map[i][c - s] = map[i + 1][c - s];
        for (int j = c - s; j < c + s; j++)
            map[r + s][j] = map[r + s][j + 1];
        for (int i = r + s; i > r - s; i--)
            map[i][c + s] = map[i - 1][c + s];
        for (int j = c + s; j > c - s; j--)
            map[r - s][j] = map[r - s][j - 1];
        map[r - s][c - s + 1= temp;
        s--;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> k;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> map[i][j];
    for (int i = 0; i < k; i++)
    {
        int a, b, c; cin >> a >> b >> c;
        turn.push_back(make_pair(a-1,make_pair(b-1,c)));
    }
    copy_map();//맵을복사.
    sort(turn.begin(), turn.end());//permutation 을사용하기위한 sort.
    do 
    {
        for (int i = 0; i < turn.size(); i++)
        {
            int r = turn[i].first;
            int c = turn[i].second.first;
            int s = turn[i].second.second;
            turning(r, c, s);
        }
        check_minnum_sum();
        recover_map();//원본을가져옴.
    } while (next_permutation(turn.begin(), turn.end()));
    //벡터에담긴 회전연산의 순서를 전부돌아본다 ->next_permutation
    //각연산은 한번씩적용.
    cout << res;
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

'Problem Solving > 삼성 역량테스트' 카테고리의 다른 글

BOJ-14500 테트로미노  (0) 2019.11.07
BOJ-17837 새로운 게임2  (0) 2019.11.06
BOJ-12100 2048(Easy)  (0) 2019.11.06
BOJ-17144 미세먼지 안녕  (0) 2019.11.06
BOJ-17780 새로운 게임  (0) 2019.11.05

도형의 종류별로 회전과 대칭의 index를 확인해보며 넣었을 경우 얻는 값을 최대로 경신해주기만 하면 풀리는 막일 문제였다. 꼼꼼하게 체크할게 거의 없었고 가능하면 정리해서 코드를 짜야 하지만 역량테스트를 치른다는 가정하에 가장 빠르게 해결하기 위하여 너저분하게 코드가 짜였다.

https://github.com/ukjinlee66/BOJ/blob/master/14500.cpp

 

ukjinlee66/BOJ

baekjoon. Contribute to ukjinlee66/BOJ development by creating an account on GitHub.

github.com

https://www.acmicpc.net/problem/14500

 

14500번: 테트로미노

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다. 정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다. 아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누

www.acmicpc.net

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int map[501][501];
int n,m;
int res;
bool check(int x, int y)
{
    if (x<1 || y<1 || x>|| y>m) return false;
    return true;
}
void one(int x, int y)
{
    if (check(x + 1, y + 1&& check(x + 1, y) && check(x, y + 1))
        res = max(res, map[x + 1][y + 1+ map[x + 1][y] + map[x][y + 1+ map[x][y]);
}
void two(int x, int y)
{
    if (check(x + 1, y) && check(x + 2, y) && check(x + 3, y))
        res = max(res, map[x][y] + map[x + 1][y] + map[x + 2][y] + map[x + 3][y]);
    //회전
    if (check(x, y + 1&& check(x, y + 2&& check(x, y + 3))
        res = max(res, map[x][y] + map[x][y + 1+ map[x][y + 2+ map[x][y + 3]);
}
void three(int x, int y)
{
    if (check(x + 1, y) && check(x + 2, y) && check(x + 2, y + 1))
        res = max(res,map[x][y] + map[x + 1][y] + map[x + 2][y] + map[x + 2][y + 1]);
    //회전 3가지.
    if (check(x, y + 1&& check(x, y + 2&& check(x - 1, y + 2))
        res = max(res, map[x][y] + map[x][y + 1+ map[x][y + 2+ map[x - 1][y + 2]);
    if (check(x, y + 1&& check(x + 1, y + 1&& check(x + 2, y + 1))
        res = max(res, map[x][y] + map[x][y + 1+ map[x + 1][y + 1+ map[x + 2][y + 1]);
    if (check(x + 1, y) && check(x, y + 1&& check(x, y + 2))
        res = max(res, map[x][y] + map[x + 1][y] + map[x][y + 1+ map[x][y + 2]);
    //반전1 +회전 3가지
    if (check(x, y + 1&& check(x - 1, y + 1&& check(x - 2, y + 1))
        res = max(res, map[x][y] + map[x][y + 1+ map[x - 1][y + 1+ map[x - 2][y + 1]);
    if (check(x, y + 1&& check(x, y + 2&& check(x + 1, y + 2))
        res = max(res, map[x][y] + map[x][y + 1+ map[x][y + 2+ map[x + 1][y + 2]);
    if (check(x, y + 1&& check(x + 1, y) && check(x + 2, y))
        res = max(res, map[x][y] + map[x][y + 1+ map[x + 1][y] + map[x + 2][y]);
    if (check(x + 1, y) && check(x + 1, y + 1&& check(x + 1, y + 2))
        res = max(res, map[x][y] + map[x + 1][y] + map[x + 1][y + 1+ map[x + 1][y + 2]);
}
void four(int x, int y)
{
    if (check(x + 1, y) && check(x + 1, y + 1&& check(x + 2, y + 1))
        res = max(res, map[x][y] + map[x + 1][y] + map[x + 1][y + 1+ map[x + 2][y + 1]);
    if (check(x, y + 1&& check(x - 1, y + 1&& check(x - 1, y + 2))
        res = max(res, map[x][y] + map[x][y + 1+ map[x - 1][y + 1+ map[x - 1][y + 2]);
    if (check(x + 1, y) && check(x, y + 1&& check(x - 1, y + 1))
        res = max(res, map[x][y] + map[x + 1][y] + map[x][y + 1+ map[x - 1][y + 1]);
    if (check(x, y + 1&& check(x + 1, y + 1&& check(x + 1, y + 2))
        res = max(res, map[x][y] + map[x][y + 1+ map[x + 1][y + 1+ map[x + 1][y + 2]);
}
void five(int x, int y)
{
    if (check(x, y + 1&& check(x, y + 2&& check(x - 1, y + 1))
        res = max(res, map[x][y] + map[x][y + 1+ map[x][y + 2+ map[x - 1][y + 1]);
    if (check(x, y + 1&& check(x + 1, y + 1&& check(x - 1, y + 1))
        res = max(res, map[x][y] + map[x][y + 1+ map[x + 1][y + 1+ map[x - 1][y + 1]);
    if (check(x, y + 1&& check(x, y + 2&& check(x + 1, y + 1))
        res = max(res, map[x][y] + map[x][y + 1+ map[x][y + 2+ map[x + 1][y + 1]);
    if (check(x + 1, y) && check(x + 2, y) && check(x + 1, y + 1))
        res = max(res, map[x][y] + map[x + 1][y] + map[x + 2][y] + map[x + 1][y + 1]);
}
void sol(int x,int y,int number) //도형의 번호를받음. (1~5)
{
    switch (number)
    {
    case 1:
        one(x, y);
        break;
    case 2:
        two(x, y);
        break;
    case 3:
        three(x, y);
        break;
    case 4:
        four(x, y);
        break;
    case 5:
        five(x, y);
        break;
    }
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> map[i][j];
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            for (int k = 1; k <= 5; k++)
            {
                sol(i, j, k);
            }
        }
    }
    cout << res;
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

'Problem Solving > 삼성 역량테스트' 카테고리의 다른 글

BOJ-17406 배열돌리기 4  (0) 2019.11.10
BOJ-17837 새로운 게임2  (0) 2019.11.06
BOJ-12100 2048(Easy)  (0) 2019.11.06
BOJ-17144 미세먼지 안녕  (0) 2019.11.06
BOJ-17780 새로운 게임  (0) 2019.11.05

새로운 게임1과의 차이점이라면 더이상 맨밑의말만 움직이는것이 아닌 모든 말이 각턴에 움직이게되는데 말위에 있더라도 그놈이 업고있는놈들을데리고 말을 내려서 이동한다는 점이다. 단지 level을 없애고 vector index만 잘 조절해주면 해결되는 문제이다.

https://github.com/ukjinlee66/BOJ/blob/master/17837.cpp

 

ukjinlee66/BOJ

baekjoon. Contribute to ukjinlee66/BOJ development by creating an account on GitHub.

github.com

https://www.acmicpc.net/problem/17837

 

17837번: 새로운 게임 2

재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하나의 말 위에 다른 말을 올릴 수 있다. 체스판의 각 칸은 흰색, 빨간색, 파란색 중 하나로 색칠되어있다. 게임은 체스판 위에 말 K개를 놓고 시작한다. 말은 1번부터 K번까지 번호가 매겨져 있고, 이동 방향도 미리 정해져 있다. 이동 방향은 위, 아래, 왼쪽, 오른쪽

www.acmicpc.net

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include<iostream>
#include<vector>
#include<deque>
#include<algorithm>
using namespace std;
int n, k;
int map[13][13];
vector<int> M[13][13];//2차원 공간관리.
pair<intint> dir[4= { {0,1},{0,-1},{-1,0},{1,0} }; //-> <- ^ v
vector<pair<intpair<intint>>> hor;//말의 좌표와 방향관리.
void checksize()
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            cout << M[i][j].size() << " ";
        cout << "\n";
    }
}
bool checkturn()
{
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            if (M[i][j].size() >= 4//사이즈가4이상인 말이존재할경우.
            {
                return true;
            }
    return false;
}
void moving(int idx)//말의번호.
{
    int curx = hor[idx].first; //x좌표
    int cury = hor[idx].second.first; // y좌표
    int mo = hor[idx].second.second; //방향
    int nx = curx + dir[mo].first;//이동하려는 x좌표
    int ny = cury + dir[mo].second;//이동하려는 y좌표
    int w = map[nx][ny];
    if (w == 2 || (nx < 0 || ny < 0 || nx >= n || ny >= n))//파랑 일경우 반대바꿔서이동.
    {
        //방향전환.
        if (mo == 0) hor[idx].second.second = 1;
        else if (mo == 1) hor[idx].second.second = 0;
        else if (mo == 2) hor[idx].second.second = 3;
        else hor[idx].second.second = 2;
        mo = hor[idx].second.second; //바뀐방향 업데이트.
        nx = curx + dir[mo].first;
        ny = cury + dir[mo].second;//새로운이동.
        if ((nx < 0 || ny < 0 || nx >= n || ny >= n) || map[nx][ny] == 2)//파랑이거나 범위를넘어간경우.
            return;//방향을바꿧으니 그대로종료.
        else //파랑이거나 범위밖이아니므로 이동.
            moving(idx);
    }
    else if (w == 1)//빨강 덱을뒤집는다.
    {
        //말의좌표와공간을 이동시킨후 공간을 뒤집는다.
        int id = 0int SSize = M[nx][ny].size();
        if (M[nx][ny].size() >= 1//빨간공간의 말이 이미 존재할경우.
        {
            for (int i = M[curx][cury].size()-1;; i--//빼면서 현재말의방향으로 전부이동시킨다. 현재칸의뒤에서빼서 뒤로집어넣음.
            {
                int val = M[curx][cury][i];
                M[nx][ny].push_back(val);
                if (M[nx][ny].size() >= 4)
                    return;
                hor[val].first = nx;
                hor[val].second.first = ny; //말의좌표를바꾸어준다.
                if (M[curx][cury][i] == idx)
                {
                    id = i;
                    break;
                }
            }
            M[curx][cury].erase(M[curx][cury].begin() + id, M[curx][cury].end());
        }
        else //없을경우.
        {
            bool flag = falseint id = 0;
            for(int i=0;i<M[curx][cury].size();i++//빼면서 현재말의방향으로 전부이동시킨다. //밑에서빼서 아래로집어넣음.
            {
                if (M[curx][cury][i] == idx) {
                    flag = true; id = i;
                }
                if (flag) 
                {
                    int val = M[curx][cury][i];
                    M[nx][ny].push_back(val);
                    if (M[nx][ny].size() >= 4)
                        return;
                    hor[val].first = nx;
                    hor[val].second.first = ny; //말의좌표를바꾸어준다. 그 후 꼭대기있던놈만 바닥으로간다.
                }
            }
            M[curx][cury].erase(M[curx][cury].begin() + id, M[curx][cury].end());
            reverse(M[nx][ny].begin(), M[nx][ny].end());//공간을뒤집어줌.
        }
    }
    else//하양
    {
        if (M[nx][ny].size() >= 1)//말이 이미 존재할경우.
        {
            bool flag = falseint id = 0;
            for (int i = 0; i<M[curx][cury].size();i++)//현재칸의 공간에서 현재말의 idx포함 그 뒤를 전부비움
            {
                if (M[curx][cury][i] == idx) {
                    flag = true;
                    id = i;
                }
                if (flag)
                {
                    int val = M[curx][cury][i];
                    M[nx][ny].push_back(val);//새로운칸에 말을 쌓아올림.
                    if (M[nx][ny].size() >= 4)
                        return;
                    //말의 좌표변경 방향은 그대로둔다.
                    hor[val].first = nx;
                    hor[val].second.first = ny;
                }
            }
            M[curx][cury].erase(M[curx][cury].begin() + id, M[curx][cury].end());
        }
        else //말이존재하지않을경우그대로이동. 
        {
            bool flag = falseint id = 0;
            for (int i = 0; i<M[curx][cury].size(); i++)//현재칸의 공간에서 현재말의 idx포함 그 뒤를 전부비움
            {
                if (M[curx][cury][i] == idx) {
                    flag = true;
                    id = i;
                }
                if (flag)
                {
                    int val = M[curx][cury][i];
                    M[nx][ny].push_back(val);//새로운칸에 말을 쌓아올림.
                    if (M[nx][ny].size() >= 4)
                        return;
                    //말의 좌표변경 방향은 그대로둔다.
                    hor[val].first = nx;
                    hor[val].second.first = ny;
                }
            }
            M[curx][cury].erase(M[curx][cury].begin() + id, M[curx][cury].end());
        }
    }
    return;
}
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> k;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> map[i][j];
    //이동방향 -> <- ^ v 1 2 3 4
    //체스판 0흰 1빨 2파
    for (int i = 0; i < k; i++//체스를 받음.
    {
        int row, column, move;
        cin >> row >> column >> move;
        move--;
        row--;
        column--;
        hor.push_back({ row,{column,move} }); //말의 좌표와 방향을저장.
        M[row][column].push_back(i); //체스판의 공간에 할당.(i번)
        //같은칸에 말이두개이상 입력으로주어지지않는다.
    }
    int turn = 1;
    while (true)
    {
        /*checksize();
        puts("");*/
        if (turn >= 1000) {
            turn = -1;
            break;
        }
        for (int i = 0; i < k; i++) {//말을돌아보며 턴을시작.
            moving(i);
            if (checkturn()) //만약 높이가4이상인말이있을경우 끝냄.
                break;
        }
        
        if (checkturn()) //만약 높이가4이상인말이있을경우 끝냄.
            break;
        turn++;//아닐경우 턴을증가.
    }
    cout << turn;
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

'Problem Solving > 삼성 역량테스트' 카테고리의 다른 글

BOJ-17406 배열돌리기 4  (0) 2019.11.10
BOJ-14500 테트로미노  (0) 2019.11.07
BOJ-12100 2048(Easy)  (0) 2019.11.06
BOJ-17144 미세먼지 안녕  (0) 2019.11.06
BOJ-17780 새로운 게임  (0) 2019.11.05

+ Recent posts