Section 2.1 专题乱乱的

TEXT里简单介绍了一下图论和Flood Fill Algorithms,感觉还是很有意义的,可以看看,http://www.wzoi.org/usaco/也是比较正常的翻译了,看起来不是机翻

 

The Castle

http://www.wzoi.org/usaco/12%5C105.asp

大致就是给你一个矩阵,依次描述每个点四面的墙的情况,点点可达到联通成一个房间,有墙不能穿过,问:

有多少个房间?

最大的房间的大小

删去一面墙后最大房间是多少?删去哪个墙?

 

简单BFS,标记点就可以,应该就是FF的应用吧。

处理四边走的方向的时候加上位运算就比较方便patpat。所以GO数组的顺序要讲究。

搜索的过程中可以得到每个房间的大小。选最大即第二个问题的解

第三个问题:枚举每个点的每个方向,如果该方向上有墙,且墙的另一边属于的房间不同就处理

第四个问题要求最靠西,最靠南 所以枚举的时候加个条件或者换一下枚举的顺序就可以了。

#include <bits/stdc++.h>
using namespace std;
const int N = 55;
int n, m;
int gox[4] = {0, -1, 0, 1};
int goy[4] = {-1, 0, 1, 0};
int op[N][N], Size[N*N], vis[N][N];
int id = 0;
struct point{
    int x, y;
    point(){};
    point(int  _x, int _y){
        x = _x; y = _y;
    }
};

bool outmap(int x, int  y){
    return x < 1 || y < 1 || x > n || y > m;
}
void bfs(int x, int y){
    int xx, yy, i;
    queue<struct point>q;
    q.push(point(x , y));
    point a;
    vis[x][y] = ++id;
    Size[id] = 1;
    while(!q.empty()){
        a  = q.front(); q.pop();
        x = a.x;    y = a.y;
        for(i = 0 ;i < 4; i++){
            if(op[x][y] &(1<<i))    continue;
            xx = gox[i] + x;    yy = goy[i] + y;
            if(outmap(xx, yy))continue;
            if(vis[xx][yy])continue;
            vis[xx][yy] = id;
            Size[id] ++;
            q.push(point(xx, yy));
        }
    }
}
int main()
{

    freopen("castle.in","r",stdin);
    #ifndef poi
    freopen("castle.out","w",stdout);
    #endif
    int i, j, k, x, y;
    scanf("%d%d", &m, &n);
    for(i = 1; i <= n; i++){
        for(j = 1; j <= m; j++){
            scanf("%d",&op[i][j]);
        }
    }

    for(i = 1; i <= n; i++){
        for(j = 1; j <= m; j++){
            if(vis[i][j]) continue;
             bfs(i, j);
        }
    }
    int ans = 0, maxroom = 0, posx, posy, posdir;
    for(i = 1; i <= n; i++){
        for(j = 1; j <= n; j++){
            maxroom = max(maxroom, Size[vis[i][j]]);
            for(k = 0; k < 4; k++){
                if(op[i][j]&(1<<k)){
                    x = i + gox[k];y = j + goy[k];
                    if(outmap(x, y))    continue;
                    if(vis[i][j] == vis[x][y])continue;
                    int temp = Size[vis[x][y]] + Size[vis[i][j]];
                    if(temp > ans){
                        posx = i;   posy = j;   posdir = k;
                        ans = temp;
                    }
                    else if(temp == ans){
                   //     cout << ans << " " << i << " "<<j <<endl;
                        if(j < posy || (j == posy && i > posx)){
                            posx = i;   posy = j;   posdir = k;
                        }
                    }
                }
            }
        }
    }
    cout << id <<endl<<maxroom <<endl << ans << endl ;
    cout << posx <<" "<<posy <<" ";
    switch(posdir){
        case 0: cout << "W"<<endl;  break;
        case 1: cout << "N"<<endl;  break;
        case 2: cout << "E" << endl;    break;
        case 3: cout << "S" <<endl; break;
    }
    return 0;

}
View Code

 

Ordered Fractions

按顺序输出分母<=N的所有既约真分数

题解的标准方法是搜索,但是我爆搞了= =

枚举分母,据此枚举分子,用gcd判断是否互素,再排序。

排序的时候用分母分子相乘而不是变成小数的方法来保证精度。

#include <bits/stdc++.h>
using namespace std;
struct point{
    int x, y;
    point(){};
    point(int _x, int _y){x = _x, y = _y;};
    bool operator<(const point &I)const{
        if(y == I.y)return x <I.x;
        return x*I.y < y * I.x;
    }
};
vector<struct point>ans;
int main()
{
    freopen("frac1.in","r",stdin);
    #ifndef poi
    freopen("frac1.out","w",stdout);
    #endif
    int n, i, j;
    cin >> n;
    ans.push_back(point(0, 1));    ans.push_back(point(1, 1));
    for(i = 2; i <= n; i++){
        for(j = 1; j < i; j++){
            if(__gcd(i, j) != 1)    continue;
            ans.push_back(point(j, i));
        }
    }
    sort(ans.begin(), ans.end());
    for(i = 0; i < ans.size(); i++){
        printf("%d/%d\n", ans[i].x, ans[i].y);
    }
    return 0;
}
View Code

Sorting a Three-Valued Sequence 

很有趣的题patpat

给出一堆由1 2 3组成的序列,交换两个数为一次操作,问最少需要多少次操作可以使得序列有序

 

输入的时候可以处理出多少个1 2 3

如果1的位置在前num[1]个就不用交换,同理23

故扫一次可以扫出有多少个1放在了应该是2在的地方,多少个2在1的地方之类的,用二维数组描述。所有的位置不对的数目为sum

如果1占了2的位置2占了1的位置,把他们交换一次即可使sum -=2

同理13 23.

当他们互相之间的占领关系形成一个环,则需要交换两次使sum-=3

 

所以先搞完所有一次交换的,sum-2*交换次数则为需要交换两次的。

#include <bits/stdc++.h>
using namespace std;
const int N  = 1004;
int a[N];
int num[4];
int disor[4][4];
int main()
{
    freopen("sort3.in","r",stdin);
    #ifndef poi
    freopen("sort3.out","w",stdout);
    #endif
    int i, j, n, ans, sum = 0;
    scanf("%d", &n);
    for(i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        num[a[i]] ++;
    }
    for( i = 1; i <= num[1]; i++){
        disor[1][a[i]] ++;
        if(a[i] != 1) sum ++;
    }
    for( ; i <= num[1] + num[2]; i++) {
        disor[2][a[i]] ++;
        if(a[i] != 2) sum ++;
    }
    for(; i <= n; i++){
        disor[3][a[i]] ++;
        if(a[i] != 3)   sum ++;
    }
    //cout << sum << endl;
    ans = min(disor[3][1], disor[1][3]) + min(disor[2][3], disor[3][2])
            + min(disor[1][2] , disor[2][1]);
    //cout <<ans <<endl;
    ans = (sum - ans*2) * 2 / 3 + ans;
    cout << ans<<endl;
    return 0;
}
View Code

Healthy Holsteins

http://www.wzoi.org/usaco/15%5C101.asp

状压+广搜写的。从二进制中只有一个1的往队列里加,这样自然是让他用的饲料种类最少

#include <bits/stdc++.h>
using namespace std;
const int N = 30;
const int M = 40000;
int dis[M], f[N][N], need[N], get[N];
int n, m;
vector<int>ans;

bool check(int x){
    int i, j;
    memset(get, 0, sizeof(get));
    for(i = 1; i <= n; i++){
        if(x & (1 << (i-1))){
            for(j = 1; j <= m; j++){
                get[j] += f[i][j];
            }
        }
    }
    for(j = 1; j <= m; j++){
       // printf("%d %d\n",x, get[j]);
        if(need[j] > get[j])return false;
    }
    return true;
}
int bfs(int &state){
    int i, j, x,  y;
    queue<int>q;
    q.push(0);
    dis[0] = 1;
    while(!q.empty()){
        x = q.front();  q.pop();
        for(i = 1; i <= n; i++){
            if(x&(1<<(i-1)))    continue;
            y = (x |1<<(i-1));
            if(dis[y])  continue;
            dis[y] = dis[x] + 1;
            if(check(y)){
             state = y;
             return dis[y];
            }
          //  cout << y<<endl;
            q.push(y);
        }
    }
}
int main()
{
    freopen("holstein.in","r",stdin);
    #ifndef poi
    freopen("holstein.out","w",stdout);
    #endif
    int i, j;
    scanf("%d", &m);
    for(i = 1; i <= m; i++) scanf("%d", &need[i]);
    scanf("%d", &n);
    for(i = 1; i <= n; i++)
        for(j = 1; j <= m; j++) scanf("%d", &f[i][j]);
    int state;
    int ans = bfs(state);
    cout << ans - 1;
    for(i = 1; i <= n; i++){
        if(state & (1 << (i-1)))cout <<" " << i;
    }
    cout << endl;return 0;
}
View Code

Hamming Codes

http://www.wzoi.org/usaco/15%5C311.asp

要使最小,故从小到大枚举。

只要与已经选出的数都符合条件,就选中。

处理两个数差异的方法是i^j,然后gao之

这是上一节讲的位运算的方法,放在这里难道是艾宾浩斯?

#include <bits/stdc++.h>
using namespace std;
const int N  = 400;
int dis[N];
int ans[N];

int gao(int x){
    int num = 0;
    while(x){
        num += (x & 1);
        x >>= 1;
    }
    return num;
}
int main()
{
    freopen("hamming.in","r",stdin);
    #ifndef poi
    freopen("hamming.out","w",stdout);
    #endif
    dis[0] = 0;
    int n, b, d, limit, id = 1, row = 1, i, j;
    cin >> n >> b >> d;
    limit = 1 << b;
    for(i = 1; i < limit; i++){
        dis[i] = gao(i);

    }
    ans[1] = 0; cout << 0;
    for(i = 1; i < limit; i++){
        for(j = 1; j <= id; j++){
           // cout << i << " " <<ans[j] << " " <<gao(i ^ ans[j]) << endl;
            if(gao(i^ans[j])< d) break;
        }
        if(j == id + 1) {
            ans[++id] = i;
            if(row == 10){
                cout << endl<< i;
                row = 1;
            }else {
                row ++;
                cout << " " << i;
            }
            if(id == n) break;
        }
    }
    cout << endl;
    return 0;
}
View Code

 

在图书馆的计算机架子上,看到好多的计算机方面的书。。。好想看,不想回学校了。。。

寒假考研结束泡图书馆吧patpat

 

 

转载于:https://www.cnblogs.com/bbbbbq/p/4652916.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值