มีน้ำบนหน้าต่างของฉัน


13

สถานการณ์

ฉันขับรถไปตามถนนด้วยรถของฉันและฝนก็เริ่มตก เม็ดฝนตกลงบนหน้าต่างของฉันแบบสุ่มและตอนนี้ฉันถามตัวเองว่าพื้นที่เปียกชื้นที่ใหญ่ที่สุดเชื่อมต่อกันที่ไหน?

งาน

เพื่อให้ง่ายขึ้นหน้าต่างถูกแบ่งเป็นเมทริกซ์ 10 * 10 กำลังสอง งานของคุณคือการหาพื้นที่ปล่อยน้ำที่ใหญ่ที่สุดบนหน้าต่าง

อินพุต

มีอินพุตที่เป็นไปได้สองแบบคุณสามารถใช้อาร์เรย์ 2 มิติหรือ 1 มิติ คุณสามารถเลือกระหว่างอินพุตใด ๆ เช่น stdin ฯลฯ ...
ตัวอย่าง:

// 2-dimensional:
[[0,1,0,0,0,0,1,0,0,0],
 [0,1,1,0,0,0,0,1,1,0],
 [0,1,1,0,0,0,0,1,0,0],
 [0,1,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,1,0],
 [0,0,0,1,1,0,0,0,1,0],
 [0,0,0,1,1,0,0,0,1,0],
 [0,0,0,0,0,1,1,0,1,0],
 [0,0,0,0,0,1,1,0,1,0],
 [0,0,0,0,0,0,0,0,0,0]]

// 1-dimensional
[0,1,0,0,0,0,1,0,0,0,
 0,1,1,0,0,0,0,1,1,0,
 0,1,1,0,0,0,0,1,0,0,
 0,1,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,1,0,
 0,0,0,1,1,0,0,0,1,0,
 0,0,0,1,1,0,0,0,1,0,
 0,0,0,0,0,1,1,0,1,0,
 0,0,0,0,0,1,1,0,1,0,
 0,0,0,0,0,0,0,0,0,0]

เอาท์พุต

รหัสของคุณจะต้องกำหนดขนาดของพื้นที่เชื่อมต่อที่ใหญ่ที่สุดและพิกัด x และ y- ของ waterdrops ซึ่งอยู่ในพื้นที่นี้ในรูปแบบ
"ขนาด: Z พิกัด: (X1, Y1) (X2, Y2) .. ."
ตัวอย่างสำหรับการป้อนข้อมูลก่อนหน้านี้:

Size: 6 Coordinates: (1,0) (1,1) (2,1) (1,2) (2,2) (1,3)

คำสั่งของพิกัดไม่สำคัญ

กฎระเบียบ

  • มีการเชื่อมต่อ waterdrops ถ้าพวกเขาสัมผัสกัน orthogonally
  • การเชื่อมต่อในแนวทแยงจะไม่นับ
  • อาจมีหลายพื้นที่และรหัสของคุณต้องค้นหาที่ใหญ่ที่สุด
  • ฟิลด์ว่างจะแสดงเป็น "0" และฟิลด์เปียกเป็น "1"
  • โพสต์โซลูชันของคุณด้วยคำอธิบายสั้น ๆ และผลลัพธ์ของอินพุตก่อนหน้า
  • รหัสที่สั้นที่สุดใน 7 วันข้างหน้าจะเป็นผู้ชนะ
  • หากมีสองพื้นที่ที่มีขนาดเท่ากันคุณสามารถเลือกหนึ่งพื้นที่

ผู้ชนะ: Ventero กับ 171 - Ruby


2
@ Doorknob บ่นเกี่ยวกับการพิมพ์ผิดหรือเปล่า? OP คือภาษาเยอรมัน
edc65

1
@ Doorknob ฉันเปลี่ยนขอบคุณ เวลาจะบอกแค่ว่าฉันจะตัดสินผู้ชนะ แต่คุณยังสามารถโพสต์คำตอบได้
izlin

6
ผมว่าคนนี้เป็นซ้ำcodegolf.stackexchange.com/questions/32015/...
Howard

1
@Tununpronk: OP หมายถึงโปสเตอร์ต้นฉบับ ค้นหาใน Google :)
justhalf

2
ความกระจ่างบางอย่างเกี่ยวกับวิธีการป้อนข้อมูลที่ได้รับอนุญาตอย่างแน่นอนจะดีมาก
Ventero

คำตอบ:


3

Ruby, 171 ตัวอักษร

r=eval *$*
u=(0..99).map(&v=->p{-~p*r[p]>0?" (#{r[p]=0;u=p%c=10},#{p/c})"+v[p+c]+v[p-c]+v[u>0?p-1:p]+v[u<9?p+1:p]:""}).max_by &:size
puts"Size: #{u.size/6} Coordinates:"+u

อินพุตผ่านพารามิเตอร์บรรทัดคำสั่งเป็นอาร์เรย์หนึ่งมิติ

เอาต์พุตสำหรับอินพุตตัวอย่าง: Size: 6 Coordinates: (1,0) (1,1) (1,2) (1,3) (2,2) (2,1)

คำตอบนี้ใช้วิธีการเติมน้ำท่วมอย่างง่ายสร้างรายการพิกัดสำหรับแต่ละหยดน้ำฝน รหัสส่วนใหญ่จะใช้จริงสำหรับการตรวจสอบขอบเขตและ I / O


5

Python - 192

a=10;g+=[0]*a
def f(k):
 if g[k]:g[k]=0;return" (%d,%d)"%(k/a,k%a)+f(k+a)+f(k-a)+f(k+(k%a<9))+f(k-(k%a>0))
 return''
m=max(map(f,range(100)),key=len)
print"Size: "+`len(m)/6`+" Coordinates:"+m

อินพุต (วางหน้าโค้ด):

g=[0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0]

ขอบคุณงานอดิเรกของ Calvin สำหรับการแก้ไขที่แนะนำ!


คุณสามารถใช้map(f,range(100))แทน[f(i)for i in range(100)]การบันทึกได้ 8 ตัวอักษร ฉันเชื่อว่าพิกัดของคุณคือ (y, x) ไม่ใช่ (x, y)
งานอดิเรกของ Calvin

3

C # - 548 523 522 511 503 476

(ต่ำกว่า 500 ... ใช่)

ฉันแน่ใจว่ามีพื้นที่มากมายสำหรับการปรับปรุง

วิธีที่ฉันป้อนข้อมูลคือการเริ่มต้นอาร์เรย์ ฉันไม่ได้รวมอาเรย์นั้นไว้ในคะแนน (ถ้าคุณคิดว่านี่เป็นการโกงฉันสามารถเปลี่ยนรหัสได้ แต่มันจะเพิ่มรหัสที่ค่อนข้างเยอะเพราะ C # ไม่ค่อยดีในการแยกวิเคราะห์อาร์เรย์)

using o=System.Console;using l=System.Collections.Generic.List<int[]>;class P{
static int[,] a ={{0,1,0,0,0,0,1,0,0,0},{0,1,1,0,0,0,0,1,1,0},{0,1,1,0,0,0,0,1,0,0},{0,1,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,1,0},{0,0,0,1,1,0,0,0,1,0},{0,0,0,1,1,0,0,0,1,0},{0,0,0,0,0,1,1,0,1,0},{0,0,0,0,0,1,1,0,1,0},{0,0,0,0,0,0,0,0,0,0}};
static int w=10,h=w,x=0,y;static l t=new l(),m=new l();static void f(int r,int c){if(a[r,c]==1){a[r,c]=0;if(r<h)f(c,r+1);if(r>0)f(c,r-1);if(c<w)f(c+1,r);if(c>0)f(c-1,r);t.Add(new[]{c,r});}}static void Main(){for(;++x<w;)for(y=0;++y<h;){if(a[x,y]==1)f(x,y);if(t.Count>m.Count)m=t.FindAll(r=>true);t.Clear();}o.Write("Size: "+m.Count+" Coordinates: ");m.ForEach(c=>o.Write("({0},{1}) ",c[0],c[1]));}}

ทดสอบที่http://ideone.com/UCVCPM

หมายเหตุ: รุ่นปัจจุบันไม่ทำงานใน ideone เพราะไม่ชอบusing l=System.Collections...ดังนั้นรุ่น ideone จึงล้าสมัยเล็กน้อย (และอีกต่อไป)

มันทำงานอย่างไร

1มันเป็นพื้นการตรวจสอบหากมีการ หากพบหนึ่งก็ใช้อัลกอริทึมเติมน้ำท่วมเพื่อแทนที่ทั้งหมดที่อยู่ติดกัน1ด้วย0และเพิ่มพิกัดแทนที่ในรายการชั่วคราว หลังจากนั้นจะเปรียบเทียบรายการบนสุด ( m) กับรายการชั่วคราว ( t) และตั้งค่าmเป็นtหากtมีองค์ประกอบเพิ่มเติม


3

Mathematica - 180 ไบต์

ฟังก์ชันนี้ใช้อาร์เรย์ 2 มิติ

แข็งแรงเล่นกอล์ฟ

f@x_:=(c=MorphologicalComponents[x,CornerNeighbors->False];m=Last@SortBy[ComponentMeasurements[c,"Count"],Last];Print["Size: ",Last@m," Coordinates: ",Reverse/@Position[c,m[[1]]]])

น่ารัก

f@x_ := (
   c = MorphologicalComponents[x, CornerNeighbors -> False];
   m = Last@SortBy[ComponentMeasurements[c, "Count"], Last];
   Print["Size: ", Last@m, " Coordinates: ", Reverse/@Position[c, m[[1]]]]
   );

ตัวอย่าง

{0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0};
w=Partition[%,10];
f@w

ขนาด: 6 พิกัด: {{1,2}, {2,2}, {2,3}, {3,2}, {3,3}, {4,2}}

ผลลัพธ์ผิดปกติเล็กน้อย Mathematica เริ่มสร้างดัชนีที่ 1 แทน 0 และใช้{}เพื่อระบุตำแหน่ง เพิ่ม 2 ไบต์ ( -1) ถ้าตำแหน่งต้องมีดัชนี 0 เพิ่มไบต์จำนวนมากหากต้องการใช้()แทน{}:(

คำอธิบาย

fxเป็นหน้าที่ของ มันกำหนดcเป็นการแปลงของxที่แต่ละ (i, j) ตอนนี้เท่ากับจำนวนเต็มที่สอดคล้องกับองค์ประกอบที่เชื่อมต่อมันเป็นของ มันเกี่ยวข้องกับงานหลัก:

MorphologicalComponents[w, CornerNeighbors -> False] // Colorize

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นmคำนวณจำนวนองค์ประกอบในแต่ละองค์ประกอบเรียงลำดับตามจำนวนนั้นและรับผลลัพธ์สุดท้าย (ความหมายกับองค์ประกอบส่วนใหญ่) บรรทัดสุดท้ายพิมพ์นับและตำแหน่งในดัชนีที่มีอยู่ในcm


2

Haskell, 246

r=[0..9]
q=[(i,j)|i<-r,j<-r]
t v p@(i,j)|elem p v||notElem p q||g!!j!!i==0=v|1<2=foldr(\(k,l)v->t v(i+k,j+l))(p:v)$zip[-1,0,1,0][0,-1,0,1]
(?)=map
a=t[]?q;(b,c)=maximum$zip(length?a)a
main=putStrLn.unwords$["Size:",show b,"Coordinates:"]++show?c

อินพุต

สองมิติและวางก่อนโค้ดgดังนี้:

g = [[0, 1, 1, ......, 0], [......], ....]

Ungolfed

field = [
 [0,1,0,0,0,0,1,0,0,0],
 [0,1,1,0,0,0,0,1,1,0],
 [0,1,1,0,0,0,0,1,0,0],
 [0,1,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,1,0],
 [0,0,0,1,1,0,0,0,1,0],
 [0,0,0,1,1,0,0,0,1,0],
 [0,0,0,0,0,1,1,0,1,0],
 [0,0,0,0,0,1,1,0,1,0],
 [0,0,0,0,0,0,0,0,0,0]
 ]
range = [0..9]
positions = [(i, j) | i <- range, j <- range]
directions = zip [-1, 0, 1, 0] [0, -1, 0, 1]
traverse visited pos@(i, j)
  | pos `elem` visited || pos `notElem` positions || field!!j!!i == 0 = visited
  | otherwise = foldr folder (pos:visited) directions
  where folder = (\(di, dj) visited -> traverse visited (i + di, j + dj))
blocks = map (traverse []) positions
(maxCount, maxBlock) = maximum $ zip (map length blocks) blocks
main = putStrLn.unwords $ ["Size:", show maxCount, "Coordinates:"] ++ map show maxBlock

2

ฟังก์ชั่น C # 374byte

นี่เป็นคำตอบของฉันที่ดัดแปลงเป็นอย่างมากคุณอยู่ในห้องที่ใหญ่ที่สุดหรือไม่? . มันต้องใช้อาร์เรย์หนึ่งมิติของ ints และส่งกลับสตริงในรูปแบบที่ต้องการ มันทำงานโดยการสร้างชุด disjoint จากอินพุต (ลูปแรก), รับทราบขนาดของแต่ละเซ็ตและค้นหาเซ็ตที่ใหญ่ที่สุด (ลูปที่สอง) จากนั้นผนวกเซลล์ใด ๆ ในเซ็ตนั้นกับสตริงเอาต์พุต (ลูปที่สาม) .

static string F(int[]g){int s=10,e=0,d=s*s,a=0,b=d+1;int[]t=new int[b],r=new int[b];t[d]=d;System.Func<int,int>T=null,k=v=>t[T(v)]=t[v]<d?a:d;T=v=>t[v]!=v?T(t[v]):v;for(;a<d;a++)if(g[a]>0){e=t[a]=a;if(a>s)k(a-s);if(a%s>0)k(a-1);}else t[a]=d;for(;a-->0;)e=r[e]<++r[b=T(a)]&&b<d?b:e;var p="Size: "+r[e]+" Coordinates:";for(;d-->1;)p+=T(d)==e?" ("+d%s+","+d/s+")":"";return p;}

หัก golfed (และด้วยรหัสทดสอบ):

class P
{
    static int[] z = {0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0};

    static string F(int[]g)
    {
        int s=10,e=0,d=s*s,a=0,b=d+1;

        int[]t=new int[b],r=new int[b];
        t[d]=d;
        System.Func<int,int>T=null,k=v=>t[T(v)]=t[v]<d?a:d;
        T=v=>t[v]!=v?T(t[v]):v;

        for(;a<d;a++)
            if(g[a]>0)
            {
                e=t[a]=a;
                if(a>s)k(a-s);
                if(a%s>0)k(a-1);
            }
            else
                t[a]=d;
        for(;a-->0;)
            e=r[e]<++r[b=T(a)]&&b<d?b:e;

        var p="Size: "+r[e]+" Coordinates:";
        for(;d-->1;)
            p+=T(d)==e?" ("+d%s+","+d/s+")":"";

        return p;
    }

    static void Main()
    {
        System.Console.WriteLine(F(z));
    }
}

สิ่งนี้ทำให้ฉันรู้สึกไม่ดีเกี่ยวกับวิธีแก้ปัญหา 476 ไบต์ของฉัน :( +1 สำหรับคุณครับคุณดี
Christoph Böhmwalder

1

JavaScript (EcmaScript 6) 183 189

ฟังก์ชันที่มีอินพุตอาร์เรย์และค่าส่งคืนสตริง หากจำเป็นต้องใช้เอาต์พุตจริง (ไม่ชัดเจนสำหรับฉัน) เพิ่ม 7 ไบต์สำหรับ 'alert ()'

W=(a,n=10,x=0)=>(F=p=>a[p]|0&&(a[p]=0,o+=' ('+p%n+','+(p/n|0)+')',1+F(p-n)+F(p+n)+F(p-(p%n>0))+F(p+((p+1)%n>0))),a.map((e,p)=>(t=F(p,o=''))>x&&(x=t,k=o)),'Size: '+x+' Coordinates:'+k)

ทดสอบผลลัพธ์

Size: 6 Coordinates: (1,0) (1,1) (1,2) (1,3) (2,2) (2,1)

อ่านเพิ่มเติม

W=(a,n=10,x=0)=>
(
  F=p=>
    a[p]|0&&(  
      a[p]=0,o+=' ('+p%n+','+(p/n|0)+')',
      1+F(p-n)+F(p+n)+F(p-(p%n>0))+F(p+((p+1)%n>0)) // modulo takes care of not overflowing out of a row
    ),
  a.map((e,p)=>(t=F(p,o=''))>x&&(x=t,k=o)), 
  'Size: '+x+' Coordinates:'+k
)

คำอธิบาย

รับอาร์เรย์มิติข้อมูลเดียวและพารามิเตอร์ทางเลือกพร้อมขนาดของแถว ฟังก์ชั่นนี้ใช้งานได้กับขนาดอาเรย์ต่าง ๆ แม้แต่ x! = y

รหัสหลอก:

 For each element, 
   try to fill. 
   During the fill operation build a string with the coordiinates. 
   Remember the longest fill and the corresponding string and 
 output that at the end.

1

JavaScript, 273

ฟังก์ชั่นใช้อาร์เรย์และส่งกลับสตริง ความพยายามครั้งแรกของฉันคือ ~ 500 ตัวอักษรและไม่ได้ใช้การเติมน้ำท่วม อันนี้ทำ ฉันกำลังเรียนรู้ JavaScript ดังนั้นข้อเสนอแนะใด ๆ จะได้รับการชื่นชม

ฟังก์ชั่นนี้วนซ้ำผ่านอาร์เรย์อินพุตและสำหรับแต่ละ 1 พบว่ามันเริ่มต้นที่นั่นและเปลี่ยนแปลงทั้งหมดที่เชื่อมต่อกับ 1s เป็น 0 โดยใช้ฟังก์ชั่นเติม ในขณะที่ทำเช่นนั้นมันจะจดจำหยดที่มี 1s มากที่สุด ฟังก์ชั่นเติมเปลี่ยนตำแหน่งปัจจุบันเป็น 0 แล้วเรียกตัวเองในตำแหน่งด้านบนไปทางขวาด้านล่างและไปทางซ้าย

function G(m){var B="",b=0;for(var p=0;p<m.length;p++)if(m[p]){var T="",t=0;
Z(p);if(t>b){b=t;B=T;}}return"Size: "+b+" Coordinates:"+B;function Z(p){if(m[p])
{m[p]=0;t++;T+=" ("+p%10+","+Math.floor(p/10)+")";if(p>9)Z(p-10);if(p%10)Z(p-
1);if(p<90)Z(p+10);if(p%10!=9)Z(p+1);}}}

ทดสอบที่นี่: http://goo.gl/9Hz5OH

รหัสที่อ่านได้

var map = [0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
           ...
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

function F(map) {

    var bestBlob = "", bestLen=0;
    for (var p = 0; p < map.length; p++)
        if (map[p]) {
            var thisBlob = "", thisLen=0;
            Fill(p);
            if (thisLen > bestLen){
                bestLen=thisLen ; bestBlob = thisBlob;
            }
        }
    return "Size: " + bestLen + " Coordinates:" + bestBlob;

    function Fill(p) {
        if (map[p]) {
            map[p] = 0; thisLen++;
            thisBlob += " (" + p % 10 + "," + Math.floor(p / 10) + ")";
            if (p > 9) Fill(p - 10);
            if (p % 10) Fill(p - 1);
            if (p < 90) Fill(p + 10);
            if (p % 10 != 9) Fill(p + 1);
        }
    }
}

1

สกาลา, 420

สวัสดีรายการของฉันใช้อาร์เรย์ 2d เป็นList[List[Int]]ส่งกลับString

val o=for{(r, y)<-w.zipWithIndex;(v,x)<-r.zipWithIndex;if(v == 1)}yield(x,y);val a=o.flatMap(c=>o.collect{case(x,y)if{(x==c._1+1||x==c._1-1)&&y==c._2^(y==c._2+1||y==c._2-1)&&x==c._1}=>c->(x,y)}).groupBy(_._1).map(n => (n._1->n._2.map(t=>t._2)));val b=a.values.flatMap(v=>v.map(c=>a(c)++v));val l=b.collect{case x if (x.length==b.map(_.length).max)=>x}.head;println(s\"Size: ${l.length} Coordinates: ${l.mkString(" ")}\")

คำอธิบาย

ให้หน้าต่างเป็น a List[List[Int]]ก่อนอื่นเราจะพบแต่ละ "1" และบันทึกพิกัดในรายการ ต่อไปเราจะเปลี่ยนรายการนั้นให้เป็นMapพิกัดของแต่ละ "1" เป็นรายการพิกัดของทุก ๆ "1" ที่อยู่ติดกัน จากนั้นใช้แผนที่ adjacency เพื่อเชื่อมโยง sub-blobs เข้ากับ blobs และในที่สุดเราก็กลับ blob ที่ใหญ่ที่สุด

อ่านเพิ่มเติม

 val w = {
  List(//0,1,2,3,4,5,6,7,8,9
    List(0,1,0,0,0,0,1,0,0,0), //0
    List(0,1,1,0,0,0,0,1,1,0), //1
    List(0,1,1,0,0,0,0,1,0,0), //2
    List(0,1,0,0,0,0,0,0,0,0), //3
    List(0,0,0,0,0,0,0,0,1,0), //4
    List(0,0,0,1,1,0,0,0,1,0), //5
    List(0,0,0,1,1,0,0,0,1,0), //6
    List(0,0,0,0,0,1,1,0,1,0), //7
    List(0,0,0,0,0,1,1,0,1,0), //8
    List(0,0,0,0,0,0,0,0,0,0)) //9
}

case class Coord(x: Int, y: Int)

val ones: List[Coord] = for{
  (row, y)   <- w.zipWithIndex
  (value, x) <- row.zipWithIndex
  if (value == 1)        
} yield Coord(x,y)

val adjacencyMap: Map[Coord, List[Coord]] = ones.flatMap(keyCoord => ones.collect{
case Coord(adjacentX, adjacentY) if {
    (adjacentX == keyCoord.x + 1 || adjacentX == keyCoord.x - 1) && adjacentY == keyCoord.y ^ 
    (adjacentY == keyCoord.y + 1 || adjacentY == keyCoord.y - 1) && adjacentX == keyCoord.x
  }  => keyCoord->Coord(adjacentX,adjacentY)
}).groupBy(_._1).map(n => (n._1->n._2.map(t=>t._2) ))

val blobs: Iterable[List[Coord]] = adjacencyMap.values.flatMap(v => v.map(coord => adjacencyMap(coord)++v))

val largestBlob: List[Coord] = blobs.collect{case x if (x.length == blobs.map(b=> b.length).max) => x}.head

println(s"""Size: ${largestBlob.length} Coordinates: ${largestBlob.collect{case Coord(x,y) => (x,y)}.mkString(" ")}""")

คำติชมเป็นที่ชื่นชมอย่างมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.