1. 程式人生 > >貪心演算法(2)

貪心演算法(2)

King’s Cake

HDU 5640 http://acm.hdu.edu.cn/showproblem.php?pid=5640

It is the king’s birthday before the military parade . The ministers prepared a rectangle cake of size n×m(1≤n,m≤10000) . The king plans to cut the cake himself. But he has a strange habit of cutting cakes. Each time, he will cut the rectangle cake into two pieces, one of which should be a square cake.. Since he loves squares , he will cut the biggest square cake. He will continue to do that until all the pieces are square. Now can you tell him how many pieces he can get when he finishes.

Input
The first line contains a number T(T≤1000), the number of the testcases.

For each testcase, the first line and the only line contains two positive numbers n,m(1≤n,m≤10000).

Output
For each testcase, print a single number as the answer.

Sample Input
2
2 3
2 5
Sample Output
3
4

給你一個形狀為矩形的蛋糕,每一次把矩形切割為兩部分,其中一部分為最大的正方形,另外一部分再繼續切割直到全部都為正方形為止,求切割完之後一共有多少塊蛋糕


對於長和寬分別為n,m的矩形,一次切割面積最大的正方形的邊長x = min(m,n),剩餘部分的長寬分別為max(m,n)-x和x,然後再對子問題求解

#include<iostream>
using namespace std;

int main(){
    int t,n,m;
    int count,x,y;
    cin>>t;
    while(t--){
        cin>>n>>m;
        count = 1;
        while(m != n){
            x = m>n?m:n;
            y = m>n?n:m;
            x -= y;
            count++;
            m = x;
            n = y;
        }
        cout
<<count<<endl; } return 0; }

King’s Phone

HDU 5641 http://acm.hdu.edu.cn/showproblem.php?pid=5641

In a military parade, the King sees lots of new things, including an Andriod Phone. He becomes interested in the pattern lock screen.

The pattern interface is a 3×3 square lattice, the three points in the first line are labeled as 1,2,3, the three points in the second line are labeled as 4,5,6, and the three points in the last line are labeled as 7,8,9。The password itself is a sequence, representing the points in chronological sequence, but you should follow the following rules:

  • The password contains at least four points.
  • Once a point has been passed through. It can’t be passed through again.

  • The middle point on the path can’t be skipped, unless it has been passed through(3427 is valid, but 3724 is invalid).

His password has a length for a positive integer k(1≤k≤9), the password sequence is s1,s2…sk (0si<INTMAX) , he wants to know whether the password is valid. Then the King throws the problem to you.

Input
The first line contains a number T (0<T100000) , the number of the testcases.

For each test case, there are only one line. the first first number k,represent the length of the password, then k numbers, separated by a space, representing the password sequence s1,s2…sk.

Output
Output exactly T lines. For each test case, print valid if the password is valid, otherwise print invalid

Sample Input
3
4 1 3 6 2
4 6 2 1 3
4 8 1 6 7

類似於現在的手機屏保圖案密碼。已知螢幕上為3*3的九個數字,給你一個密碼,若存在以下情況則密碼無效:該密碼不止一次經過同一個數字;密碼長度小於4;兩個點之間跳過一個沒被經過的點

這是AC的程式碼,在這份程式碼直接之前還有很多版本。思路就是有兩個判斷函式:一個判斷密碼長度是否小於四以及該密碼是否有重複經過同一個點;第二個判斷函式判斷是否存在兩個點之間經過一個未被經過的點,這裡採用列舉,因為一共只有九個點,把中間可能會經過另外一個點的情況都列出來,例如(1,3,2)1和3之間經過了點2,還有(1,7,4),(1,9,5),(4,6,5),(7,9,8),(2,8,5),(3,7,5),(3,9,6).

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int k,a[12],vis[12];
int check1(){
    if(k<4)return 0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=k;i++){
        if(a[i]>9||a[i]<1||vis[a[i]]==1){
            return 0;
        }
        else {
            vis[a[i]]=1;
        }
    }
    return 1;
}
int check2(){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<k;i++){
        if(a[i]==1&&a[i+1]==3&&vis[2]==0)return 0;
        if(a[i]==3&&a[i+1]==1&&vis[2]==0)return 0;
        if(a[i]==1&&a[i+1]==7&&vis[4]==0)return 0;
        if(a[i]==7&&a[i+1]==1&&vis[4]==0)return 0;
        if(a[i]==1&&a[i+1]==9&&vis[5]==0)return 0;
        if(a[i]==9&&a[i+1]==1&&vis[5]==0)return 0;
        if(a[i]==3&&a[i+1]==9&&vis[6]==0)return 0;
        if(a[i]==9&&a[i+1]==3&&vis[6]==0)return 0;
        if(a[i]==7&&a[i+1]==3&&vis[5]==0)return 0;
        if(a[i]==3&&a[i+1]==7&&vis[5]==0)return 0;
        if(a[i]==7&&a[i+1]==9&&vis[8]==0)return 0;
        if(a[i]==9&&a[i+1]==7&&vis[8]==0)return 0;
        if(a[i]==4&&a[i+1]==6&&vis[5]==0)return 0;
        if(a[i]==6&&a[i+1]==4&&vis[5]==0)return 0;
        if(a[i]==2&&a[i+1]==8&&vis[5]==0)return 0;
        if(a[i]==8&&a[i+1]==2&&vis[5]==0)return 0;
        vis[a[i]]=1;
    }
    return 1;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&k);
        for(int i=1;i<=k;i++){
            scanf("%d",&a[i]);
        }
        if(check1()==0||check2()==0)cout<<"invalid"<<"\n";
        else cout<<"valid"<<"\n";
    }
    return 0;
}

報錯的版本。判斷都在主函式中進行,對於判斷兩點之間是否經過未經過的點,是按點分類然後進行判斷的,但是提交的時候報錯了

#include<stdio.h>
int s[10];
int main(){

    int k,i,flag;
    int t;
    //cin>>t;
    scanf("%d",&t);
    while(t--){

        flag = 0;
        scanf("%d",&k);
        //cin>>k;     //密碼長度

        for(i = 0;i < k;i++){
            scanf(" %d",&s[i]); 
            if(s[i] < 1 || s[i] > 9 || a[s[i]] == 1){
                flag = 1;
            }
            a[s[i]] = 1;
        }
        if(k < 4){
        flag = 1;
       }
    memset(a,0,sizeof(a));
    for(int i = 1;i <= k;i++){
        if(a[i]>9||a[i]<1||a[s[i]]==1){
            flag = 1;
        }
        else{
            a[s[i]] = 1;
        }
    }
    for(i = 1;i < k;i++){
        a[s[0]] = 0;
    }
        for(i = 1;i < k;i++){
           if(a[s[i]] == 1){
               flag = 1;
           }
           else{
                switch(s[i-1]){
                case 1: if((s[i] == 7 && a[4] == 0) || (s[i] == 9 && a[5] == 0) || (s[i] == 3 && a[2] == 0)){
                         flag = 1;
                        }
                        break;
                case 2: if(s[i] == 8 && a[5] == 0){
                           flag = 1;
                        }
                        break;
                case 3: if((s[i] == 7 && a[5] == 0) || (s[i] == 9 && a[6] == 0) || (s[i] == 1 && a[2] == 0)){
                            flag = 1;
                        }
                        break;
                case 4: if(s[i] == 6 && a[5] == 0){
                            flag = 1;
                        }
                        break;  
                case 5: break;
                case 6: if(s[i] == 4 && a[5] == 0){
                           flag = 1;
                        }
                        break;
                case 7: if((s[i] == 1 && a[4] == 0) || (s[i] == 9 && a[8] == 0) ||(s[i] == 3 && a[5] == 0)){
                            flag = 1;
                        }
                        break;
                case 8: if(s[i] == 2 && a[5] == 0){
                            flag = 1;
                        }
                        break;
                case 9: if((s[i] == 1 && a[5] == 0) || (s[i] == 3 && a[6] == 0) ||(s[i] == 7 && a[8] == 0)){
                            flag = 1;
                        }
                        break;
                }
            a[s[i]] = 1;    
            }
        }
        if(flag == 1){  
            printf("invalid\n");
        }
        else{
            printf("valid\n");
        }
    }
    return 0;
}

Radar Installation

POJ 1328 http://poj.org/problem?id=1328

Assume the coasting is an infinite straight line. Land is in one side of coasting, sea in the other. Each small island is a point locating in the sea side. And any radar installation, locating on the coasting, can only cover d distance, so an island in the sea can be covered by a radius installation, if the distance between them is at most d.

We use Cartesian coordinate system, defining the coasting is the x-axis. The sea side is above x-axis, and the land side below. Given the position of each island in the sea, and given the distance of the coverage of the radar installation, your task is to write a program to find the minimal number of radar installations to cover all the islands. Note that the position of an island is represented by its x-y coordinates.

Figure A Sample Input of Radar Installations
Input

The input consists of several test cases. The first line of each case contains two integers n (1<=n<=1000) and d, where n is the number of islands in the sea and d is the distance of coverage of the radar installation. This is followed by n lines each containing two integers representing the coordinate of the position of each island. Then a blank line follows to separate the cases.

The input is terminated by a line containing pair of zeros
Output

For each test case output one line consisting of the test case number followed by the minimal number of radar installations needed. “-1” installation means no solution for that case.
Sample Input

3 2
1 2
-3 1
2 1

1 2
0 2

0 0
Sample Output

Case 1: 2
Case 2: 1

把海岸線看成是x軸,在大海中有若干島嶼,對應著座標系上的座標,在海岸線上可安置若干雷達,買個雷達可監控範圍是以自身為中心,半徑為d的圓,求最少安置雷達的數目可覆蓋到全部島嶼
貪心策略比較難想到,可類比於區間求點,該題先找出對於每個島嶼來說能覆蓋到的海岸線上的雷達的位置的左端點和右端點,雷達一定在該區間之內。再將所有島嶼對應的左端點非降序排序,貪心策略是雷達放置在第一個島嶼對應的右端點(選擇當前情況下的最優解,將雷達放置右端可保證儘可能多覆蓋到島嶼的數量),對於第二個島嶼,若左端點大於該雷達的座標,則表示該雷達覆蓋不到第二個島嶼,第二個島嶼應另外放置一個雷達;若左端點小於該雷達座標,並且右端點也小於該雷達座標,則更換雷達位置於第二個島嶼的右端點處;否則不用更換
這道題測試了很多次,花了很久時間,然後第二份程式碼執行超時了,還是不知道問題出在哪

//AC
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef struct {
    double l, r;
}node;
node a[1000+10];
bool cmp(node a , node b){
    return a.l < b.l;
}
int main(){
    int n,d,i,cas = 0;
    while(scanf("%d%d",&n,&d) && (n || d)){  //當輸入兩個0時輸入結束
        bool run = true;           
        for(i = 0;i < n;i++){
            int tx, ty;
            scanf("%d %d", &tx, &ty);
            if(abs(ty) > d){  //如果島嶼與海岸線的直線距離>d
                run = false;
            }
            a[i].l = (double)tx - sqrt((double)(d*d - ty*ty));  //能覆蓋到該島嶼的雷達所在區間的左端  
            a[i].r = (double)tx + sqrt((double)(d*d - ty*ty));  //能覆蓋到該島嶼的雷達所在區間的右端
        }
        if(!run){
            printf("Case %d: -1\n",++cas);
            continue;
        }
        sort(a,a+n,cmp);
        int ans = 1;
        double last = a[0].r;
        for(i = 1;i < n;i++){        //貪心策略
            if(a[i].l > last){
                ans ++;
                last = a[i].r;
            }
            else if (a[i].r < last)
                last = a[i].r;
        }
        printf("Case %d: %d\n",++cas,ans);
    }
    return 0;
}

//Time Limit Exceeded
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;

/*struct Point{
    double x;   //注意不是int型,error
    double y;
    double rx,lx;   //分別是x軸上的圓心半徑所在區間的左右範圍,當點(x,y)在該圓邊上時,此時dx = sqrt(r*r-y*y),lx = x-dx,rx = x+dx
};*/

struct Point{
    double rx,lx;   //分別是x軸上的圓心半徑所在區間的左右範圍,當點(x,y)在該圓邊上時,此時dx = sqrt(r*r-y*y),lx = x-dx,rx = x+dx
};
bool compare(Point a,Point b){
    return a.lx<b.lx;
}

int main(){
    int n,d,i,count,flag,t = 0;     //Error 
    double r,x,y,dx;

    while(cin>>n>>d){       //島嶼數量與雷達的覆蓋半徑
        if(n == 0 && d == 0){  //(1<=n<=1000)
            break;
        }
        if(d<0){      //Attention
            flag = 1;
        }
        flag = 0;     //標誌位
        t++;          //表示第t個測試例項
        vector<Point> p(n);
        for(i = 0;i < n;i++){     //島嶼的幾何座標
            cin>>x>>y;
            if(fabs(y) > d){       //Attention
                flag = 1;
            }
            dx = sqrt(d*d-y*y);    //changed
            p[i].lx = x-dx;
            p[i].rx = x+dx;
        }
        if(flag){
            cout<<"Case "<<t<<": -1"<<endl;
            continue;
        }
        sort(p.begin(),p.end(),compare);     //排序,按照x座標非降序排列,x座標相等就按照y座標升序排列
        r = p[0].rx;                 //將第一個雷達位置放置在能覆蓋到第一個島嶼的座標範圍的最右端
        count = 1;                   //計數所需雷達數量
        for(i = 1;i < n;i++){
            if(p[i].lx > r){         //此時雷達範圍不能覆蓋到下一個島嶼
                count++;
                r = p[i].rx;
            }
            else{
                if(p[i].rx < r){    //此時雷達位置應重置於該島嶼的最右端  
                    r = p[i].rx;
                }
            }
        }
        cout<<"Case "<<t<<": "<<count<<endl;

    }
    return 0;
}

DZY Loves Balls

HDU 5645 http://acm.hdu.edu.cn/showproblem.php?pid=5645

DZY loves playing balls.
He has n balls in a big box. On each ball there is an integer written.

One day he decides to pick two balls from the box. First he randomly picks a ball from the box, and names it A. Next, without putting A back into the box, he randomly picks another ball from the box, and names it B.

If the number written on A is strictly greater than the number on B, he will feel happy.

Now you are given the numbers on each ball. Please calculate the probability that he feels happy.

Input
First line contains t denoting the number of testcases.

t testcases follow. In each testcase, first line contains n, second line contains n space-separated positive integers ai, denoting the numbers on the balls.
(1≤t≤300,2≤n≤300,1≤ai≤300)

Output
For each testcase, output a real number with 6 decimal places.

Sample Input
2
3
1 2 3
3
100 100 100

Sample Output
0.500000
0.000000

有若干個上面有數字編號的球,抽出第一個球,記下該球上數字大小,不放回繼續拿出第二個球,求第二個球上數字小於第一個數字的概率
貪心策略也就是數學演算法,先將輸入的數字按照非降序排序,如果沒有相同的數字的話,那麼已排序好的序列中所在位置就是比自身小的數字個數(從0計數),所以計算只要考慮到相同的數字的情況。排好序後,將某位置的數與前一個數比較,若是相同則繼續與前一個數進行大小比較,相應計數減一,一直到不相等為止.可推論公式概率大小=每個資料比它小的數/(資料總數*(資料總數-1))之和
還要注意輸出格式 printf(“%.6f\n”,s);

#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int main(){
    int t,n,i,j,k;
    //scanf("%d",&t);     //測試樣例個數
    cin>>t;
    double s;
    while(t--){
      cin>>n;
      //scanf("%d",&n);   //球的個數
      s = 0.0;
      k = 0;
      vector<int> a(n);

      for(i = 0;i < n;i++){
           cin>>a[i];
          //scanf("%d",&a[i]);
      }
      sort(a.begin(),a.end());  //非降序排序

      for(i = 1;i < n;i++){
          k = i;        //因為是非降序排序,若沒有相等的資料則可認為小於該資料的資料的個數即元素在陣列中位置
          j = i-1;
          while(a[j] == a[i] && j >= 0){  //考慮到出現相等資料的情況,從該資料往前判斷,出現一個相等的元素則減一
            k--;
            j--;
          }
          s += k;
      }
      s = s*1.0/(n-1)/n;         //可推論公式概率大小=每個資料比它小的數/(資料總數*(資料總數-1))之和
      printf("%.6f\n",s);
    }
    return 0;
}

CA Loves Stick

HDU 5655 http://acm.hdu.edu.cn/showproblem.php?pid=5655

CA loves to play with sticks.
One day he receives four pieces of sticks, he wants to know these sticks can spell a quadrilateral.
(What is quadrilateral? Click here: https://en.wikipedia.org/wiki/Quadrilateral)

Input
First line contains T denoting the number of testcases.
T testcases follow. Each testcase contains four integers a,b,c,d in a line, denoting the length of sticks.
1≤T≤1000, 0≤a,b,c,d≤263−1

Output
For each testcase, if these sticks can spell a quadrilateral, output “Yes”; otherwise, output “No” (without the quotation marks).

Sample Input
2
1 1 1 1
1 1 9 2

Sample Output
Yes
No

有四根若干長度的木棍,判斷能否組成四邊形
判定方法:最長邊大於其他三邊之和
這裡木棍長度資料範圍很大,若用int型變數肯定會超出資料範圍,所有這裡用long long型,因為三個long long型變數相加也可能超出資料範圍,所以這裡現將四根木棍長度排序,然後利用最長邊減去其中兩條邊大於第四條邊來實現

//1.0 Error
#include <stdio.h>  
int main(){  
    long long a, b, c,d;  
    int z;  
    scanf("%d", &z);  
    while (z--){  
        scanf("%d%d%d%d",&a,&b,&c,&d);  
        printf("%s\n", a+b+d>c && a+c+d>b && b+c+d>a && b+c+a>d && a>0 && b>0 && c>0 && d>0? "Yes" : "No");    //可能會超出資料範圍
    }  
    return 0;  
}
//AC 
#include<iostream>
#include<algorithm>
using namespace std;

int main(){  
    long long a[4]; 
    //int a[4];
    int t,i;  
    cin>>t;  
    while(t--){  
        for(i = 0;i < 4;i++){
            cin>>a[i];
        }
        sort(a,a+4);        //利用陣列是為了方便排序,預設為升序排序
        //if(a[3]>0 && a[2]>0 && a[1]>0 && a[0]>0 && a[3]-a[2]-a[1]<a[0]){  不加括號就是錯誤結果  Attention!!!
        if(a[3]>0 && a[2]>0 && a[1]>0 && a[0]>0 && (a[3]-a[2]-a[1]<a[0])){
            cout<<"Yes"<<endl;
        }
        else{
            cout<<"No"<<endl;
        }
    }  
    return 0;  
}