POJ3185高斯消元

简介:
The Water Bowls
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 3372   Accepted: 1315

Description

The cows have a line of 20 water bowls from which they drink. The bowls can be either right-side-up (properly oriented to serve refreshing cool water) or upside-down (a position which holds no water). They want all 20 water bowls to be right-side-up and thus use their wide snouts to flip bowls.  

Their snouts, though, are so wide that they flip not only one bowl but also the bowls on either side of that bowl (a total of three or -- in the case of either end bowl -- two bowls).  

Given the initial state of the bowls (1=undrinkable, 0=drinkable -- it even looks like a bowl), what is the minimum number of bowl flips necessary to turn all the bowls right-side-up?

Input

Line 1: A single line with 20 space-separated integers

Output

Line 1: The minimum number of bowl flips necessary to flip all the bowls right-side-up (i.e., to 0). For the inputs given, it will always be possible to find some combination of flips that will manipulate the bowls to 20 0's.

Sample Input

0 0 1 1 1 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0

Sample Output

3

Hint

Explanation of the sample:  

Flip bowls 4, 9, and 11 to make them all drinkable:  
0 0 1 1 1 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0 [initial state]  
0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0 [after flipping bowl 4]  
0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 [after flipping bowl 9]  
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [after flipping bowl 11]

Source

 
题解:
这题也是联动问题 与POJ1222相似 点击打开链接POJ1222
只是构造增广阵的方式不通 按照高消+DFS模板就可以过
 
#include <iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=230;
int a[maxn][maxn+1],x[maxn];//a 是系数矩阵和增广矩阵,x 是最后存放的解
// a[][maxn]中存放的是方程右面的值(bi)
int equ,var;//equ 是系数阵的行数,var 是系数矩阵的列数(变量的个数)
int free_num,ans=100000000;

int abs1(int num) //取绝对值
{
    if (num>=0) return num;
    else
        return -1*num;
}
void Debug(void)
{
    int i, j;
    for (i = 0; i < equ; i++)
    {
        for (j = 0; j < var + 1; j++)
        {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}
//调试输出,看消元后的矩阵值,提交时,就不用了
inline int gcd(int a, int b) //最大公约数
{
    int t;
    while (b != 0)
    {
        t = b;
        b = a % b;
        a = t;
    }
    return a;
}

inline int lcm(int a, int b) //最小公倍数
{
    return a * b / gcd(a, b);
}

int dfs(int p) //枚举自由解,只能取0-1,枚举完就回带,找到最小的
{
    if (p<=free_num-1) //深入到了主对角线元素非0 的行了
    {
        //下面就是回带的代码啊
        for(int i = free_num-1; i >= 0; --i)
        {
            int tmp = a[i][var] % 2;
            for(int j = i+1; j < var; ++j) //x[i]取决于x[i+1]--x[var]啊,所以后面的解对前面的解有影
//响。
                if(a[i][j] != 0)
                    tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2;
            x[i] = (tmp/a[i][i]) % 2; //上面的正常解
        } //回带完成了
//计算解元素为1 的个数;
        int sum=0;
        for(int i=0; i<var; i++) sum+=x[i];
        if (ans>sum) ans=sum;
        return 0;
    }
    x[p]=0;
    dfs(p-1);
    x[p]=1;
    dfs(p-1);
}
void swap(int &a,int &b)
{
    int temp=a;    //交换 2 个数
    a=b;
    b=temp;
}
int Gauss()
{
    int k,col = 0;
//当前处理的列
    for(k = 0; k < equ && col < var; ++k,++col)
    {
        int max_r = k;
        for(int i = k+1; i < equ; ++i)
            if(a[i][col] > a[max_r][col])
                max_r = i;
        if(max_r != k)
        {
            for(int i = k; i < var + 1; ++i)
                swap(a[k][i],a[max_r][i]);
        }
        if(a[k][col] == 0)
        {
            k--;
            continue;
        }
        for(int i = k+1; i < equ; ++i)
        {
            if(a[i][col] != 0)
            {
                int LCM = lcm(a[i][col],a[k][col]);
                int ta = LCM/a[i][col], tb = LCM/a[k][col];
                if(a[i][col]*a[k][col] < 0)
                    tb = -tb;
                for(int j = col; j < var + 1; ++j)
                    a[i][j] = ( (a[i][j]*ta)%2 - (a[k][j]*tb)%2 + 2 ) % 2;
                // 0 和 1 两种状态
            }
        }
    }
//a[i][j]只有

//上述代码是消元的过程,行消元完成
//解下来 2 行,判断是否无解
//注意 K 的值,k 代表系数矩阵值都为 0 的那些行的第 1 行
    for(int i = k; i < equ; ++i)
        if(a[i][col] != 0)
            return -1;
//Debug();

//唯一解或者无穷解,k<=var
//var-k==0 唯一解;var-k>0 无穷多解,自由解的个数=var-k
//能执行到这,说明肯定有解了,无非是 1 个和无穷解的问题。
//下面这几行很重要,保证秩内每行主元非 0,且按对角线顺序排列,就是检查列
    for(int i = 0; i <equ; ++i)//每一行主元素化为非零
        if(!a[i][i])
        {
            int j;
            for(j = i+1; j<var; ++j)
                if(a[i][j])
                    break;
            if(j == var)
                break;
            for(int k = 0; k < equ; ++k)
                swap(a[k][i],a[k][j]);
        }
// ----处理保证对角线主元非 0 且顺序,检查列完成

    free_num=k;
    if (var-k>0)
    {
        dfs(var-1);
        return ans;
        //无穷多解,先枚举解,然后用下面的回带代码进行回带;
//这里省略了下面的回带的代码;不管唯一解和无穷解都可以回带,只不过无穷解
//回带时,默认为最后几个自由变元=0 而已。
    }
    if(var-k<0)
        return -1;
// 无解返回 -1
    if (var-k==0)//唯一解时
    {
//下面是回带求解代码,当无穷多解时,最后几行为 0 的解默认为 0;
        for(int i = k-1; i >= 0; --i) //从消完元矩阵的主对角线非 0 的最后 1 行,开始往
//回带
        {
            int tmp = a[i][var] % 2;

            for(int j = i+1; j < var; ++j) //x[i]取决于 x[i+1]--x[var]啊,所以后面的解对前面的解有影响。
                if(a[i][j] != 0)
                    tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2;
            //if (a[i][i]==0) x[i]=tmp;//最后的空行时,即无穷解得
            //else
            x[i] = (tmp/a[i][i]) % 2; //上面的正常解
        }
        int sum=0;
        for(int i=0; i<var; i++)
            sum+=x[i];
        return sum;

        //回带结束了
    }
}

int main()
{
    int map[22];
    equ=var=20;
    while(scanf("%d",&map[0])!=EOF)
    {
        for(int i=1; i<20; i++)
            scanf("%d",&map[i]);
        memset(a,0,sizeof(a));
        memset(x,0,sizeof(x));
        for(int i=0; i<20; i++)
        {

            a[i][i]=1;
            if(i>0)
                a[i][i-1]=1;
            if(i<19)
                a[i][i+1]=1;
            if(map[i])
                a[i][20]=1;
        }
        int j1=Gauss();
        printf("%d\n",j1);
    }
    return 0;
}

目录
相关文章
|
6月前
Pseudoprime numbers(POJ-3641 快速幂)
Pseudoprime numbers(POJ-3641 快速幂)
poj 1185 炮兵阵地 (状态压缩dp)
如果你是刚刚开始做状态压缩dp,我建议你先看看 poj 3254 Corn Fields 这是一道比这一题更简单,更容易入门的题目。 还有在代码中我用了一个很巧妙的方法求一个数二进制数中1的个数 具体请看我博客中 x& (x - 1)==0 这篇文章 链接 。
39 1
POJ-3641,Pseudoprime numbers(快速幂)
POJ-3641,Pseudoprime numbers(快速幂)
|
并行计算 算法 Java
HDU 1874 畅通工程续【Floyd算法实现】
畅通工程续 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 53806    Accepted Submission(s): 20092 Problem Description 某省自从实行了很多年的畅通工程计划后,终于修建了很多路。
1086 0
poj-2909-哥德巴赫猜想
Description For any even number n greater than or equal to 4, there exists at least one pair of prime numbers p1 and p2 such that n = p1 + p2 This conjecture has not been proved nor refused yet.
797 0