大白书 1.2节 问题求解常见策略

来源:互联网 发布:mac 卸载flash player 编辑:程序博客网 时间:2024/05/29 10:10

大白书 1.2节 问题求解常见策略
(加~表示看了题解的)
~例题7 UVA 11464
枚举第一行的状态,然后用公式一行一行的推出下一行的状态。

#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <iostream>#include <string>#include <iostream>#include <queue>using namespace std;#define inf (1000000007)const int MAXN = 15 + 2;int g[MAXN][MAXN];int a[MAXN][MAXN];int dx[] = {0, -1, 0};int dy[] = {-1, 0, 1};int n;bool valid(int x, int y){    if(x < 0 || x >= n) return false;    if(y < 0 || y >= n) return false;    return true;}int solve(int state){    int ans = 0;    for(int i = 0 ; i < n ; i++){        if((1 << i) & state)    a[0][i] = 1;        else    a[0][i] = 0;    }    for(int i = 0 ; i < n - 1 ; i++){        for(int j = 0 ; j < n ; j++){            if(a[i][j] == 0 && g[i][j] == 1)    return inf;            if(a[i][j] != g[i][j])  ans++;            int mark = 0;            for(int k = 0 ; k < 3 ; k++){                int x = i + dx[k], y = j + dy[k];                if(valid(x, y) && a[x][y] == 1) mark++;            }            if(mark % 2 == 0)   a[i + 1][j] = 0;            else    a[i + 1][j] = 1;        }    }    for(int i = 0 ; i < n ; i++){    if(a[n - 1][i] != g[n - 1][i])  ans++;        if(a[n - 1][i] == 0 && g[n - 1][i] == 1)    return inf;    }    return ans;}int main(){    int T;    scanf("%d", &T);    for(int cas = 1 ; cas <= T ; cas++){        scanf("%d", &n);        for(int i = 0 ; i < n ; i++)    for(int j = 0 ; j < n ; j++)    scanf("%d", &g[i][j]);        int ans = inf;        for(int i = 0 ; i < (1 << n) ; i++){            ans = min(ans, solve(i));//            if(solve(i) == 1){   printf("i = %d\n", i);//                for(int j = 0 ; j < n ; j++){//                    for(int k = 0 ; k < n ; k++)    printf("%d ", a[j][k]);//                    printf("\n");//                }//                printf("\n");//            }        }        printf("Case %d: ", cas);        if(ans == inf)  printf("-1\n");        else    printf("%d\n", ans);    }    return 0;}

~例题8 UVA 1352
模拟题。枚举后面三个正方体的旋转状态(24^3),然后每个面选颜色最多的那个面作为颜色不变化的面,其余均变化。
方便起见只用两个函数——左旋函数和上旋函数。然后反复操作就可以达到24中变化的效果。
再优化一下。先把一个面转到顶上,然后再进行四次旋转就可以简化操作了。
~例题9 UVA 11210
问听哪张牌。
一张一张牌的判断就可以,具体实现就是把每张牌加入形成14张牌,判断这14张牌能否胡牌。
对于一副牌,首先排除两张相同的牌,用枚举的方法。然后判断剩下的牌。
箭牌只要判断是否形成三张即可。
风牌从前向后遍历,优先尽量消除后面的牌(形成顺子),然后再消除自身(三张)。
坑点是一类牌不能超过四张。
源码:

#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <string>#include <algorithm>#include <iostream>using namespace std;const int MAXN = 15;char op[MAXN];char dir[MAXN] = "TSWDNXBZFB";struct M{    int u, v;    int vis;    M(){}    M(int _u, int _v, int _vis){u = _u, v = _v, vis = _vis;}}ma[MAXN], ans[MAXN * MAXN];void output(M a){    int u = a.u, v = a.v;    if(u > 2){        if(u == 3)   printf("DONG");        else if(u == 4) printf("NAN");        else if(u == 5) printf("XI");        else if(u == 6) printf("BEI");        else if(u == 7) printf("ZHONG");        else if(u == 8) printf("FA");        else if(u == 9) printf("BAI");    }    else{        printf("%d", v);        if(u == 0)  printf("T");        else if(u == 1) printf("S");        else    printf("W");    }}bool data_init(){    for(int i = 0 ; i < 13 ; i++){        scanf("%s", op);        if(op[0] == '0')    return false;        int ok = 0;        for(int j = 3 ; j < 10 ; j++){            if(dir[j] == op[0]){                ma[i].u = j;                ma[i].v = -1;                ma[i].vis = 0;                ok = 1;                break;            }        }        if(ok == 0){            ma[i].v = op[0] - '0';            for(int j = 0 ; j < 3 ; j++){                if(dir[j] == op[1]){                    ma[i].u = j;                    ma[i].vis = 0;                    break;                }            }        }    }    return true;}bool cmp1(M a, M b){    if(a.u == b.u)  return a.v < b.v;    else    return a.u < b.u;}bool cmp2(M a, M b){    if(a.vis != b.vis)    return a.vis < b.vis;    else    return cmp1(a, b);}void ins(int u, int v){    ma[13] = M(u, v, 0);}int a[MAXN];int num[MAXN];int vis[MAXN];bool check2(int flag, int len){    if(flag >= 3){        if(num[0] == 3)    return true;        else    return false;    }    else{        memset(vis, 0, sizeof(vis));        for(int i = 0 ; i <= len ; i++){            if(num[i] == 0)  continue;            if(num[i] == 3){                if(i <= len - 2 && a[i + 1] == a[i] + 1 && a[i + 2] == a[i + 1] + 1 && num[i + 1] >= 3 && num[i + 2] >= 3)  num[i + 1] -= 3, num[i + 2] -= 3;                else num[i] -= 3;            }            else if(i <= len - 2){                if(num[i] == 2){                if(a[i + 1] == a[i] + 1 && a[i + 2] == a[i + 1] + 1 && num[i + 1] >= 2 && num[i + 2] >= 2)  num[i + 1] -= 2, num[i + 2] -= 2;                else    return false;                }                else{                if(a[i + 1] == a[i] + 1 && a[i + 2] == a[i + 1] + 1 && num[i + 1] >= 1 && num[i + 2] >= 1)  num[i + 1] -= 1, num[i + 2] -= 1;                else    return false;                }            }            else    return false;        }        return true;    }}bool check(int lv, int tlv){//    if(lv == 0 && tlv == 2){//        for(int i = 0 ; i <= 13 ; i++)  output(ma[i]), printf(" ");//        printf("\n");//    }    for(int i = 0 ; i < 13 ; i++){        if(ma[i].u == ma[i + 1].u && ma[i].v == ma[i + 1].v){//            if(lv == 0 && tlv == 2){//                printf("lv = %d, tlv = %d, i = %d\n", lv, tlv, i);//                for(int k = 0 ; k < 14 ; k++)   output(ma[k]), printf(" ");//                printf("\n");//            }            ma[i].vis = ma[i + 1].vis = 1;            int j = 0, cnt = 0;            while(ma[j].vis != 0)   j++;            a[cnt] = ma[j].v, num[cnt] = 1;            int v = ma[j].v, u = ma[j++].u;//            if(lv == 1 && i == 12 && tlv == 1)//                printf("jj = %d\n", j);            int ok = 1;            for(; j < 14 ; j++){                if(ma[j].vis)   continue;                if(ma[j].u != u){                    if(!check2(u, cnt)){//                        if(lv == 8 && i == 0){ printf("j = %d\n", j);//                            printf("u = %d, ma[j].u = %d\n", u, ma[j].u);//                        }                        ok = 0;                        break;                    }                    else    memset(num, 0, sizeof(num)), a[cnt = 0] = ma[j].v, num[cnt] = 1, u = ma[j].u, v = ma[j].v;                }                else{                    if(ma[j].v == v)    num[cnt]++;                    else    a[++cnt] = ma[j].v, num[cnt] = 1, v = ma[j].v;                }//                if(lv == 0 && tlv == 2){//                    printf("///***");//                    printf("i = %d, j = %d\n", i, j);//                    for(int k = 0 ; k <= cnt ; k++) printf("%2d ", a[k]);//                    printf("\n");//                    for(int k = 0 ; k <= cnt ; k++) printf("%2d ", num[k]);//                    printf("***///\n");//                    system("pause");//                }            }            if(!check2(u, cnt)){//                if(lv == 8 && tlv == 100 && i == 0){//                    printf("cnt = %d\n", cnt);//                    for(int k = 0 ; k <= cnt ; k++) printf("a = %d, num = %d\n", a[i], num[i]);//                    printf("\n");//                }                ok = 0;            }            if(ok)  return true;            ma[i].vis = ma[i + 1].vis = 0;        }    }    return false;}int main(){//    freopen("UVA 11210.in", "r", stdin);    int cas = 0;    while(data_init()){        int cnt = 0;        sort(ma, ma + 13, cmp1);        for(int i = 0 ; i < 3 ; i++){            for(int j = 1 ; j <= 9 ; j++){                int tnum = 0;                for(int k = 0 ; k < 13 ; k++)   if(ma[k].u == i && ma[k].v == j)    tnum++;                if(tnum >= 4)   continue;                ins(i, j);                sort(ma, ma + 14, cmp1);                if(check(i, j)){                    ans[cnt++] = M(i, j, 0);                }                for(int k = 0 ; k < 14 ; k++)   ma[k].vis = 0;                for(int k = 0 ; k < 14 ; k++){                    if(ma[k].u == i && ma[k].v == j){ma[k].vis = 1; break;}                }//                if(i == 0){//                    printf("\n//***\n");//                    printf("j = %d\n", j);//                    for(int k = 0 ; k < 14 ; k++)   output(ma[k]), printf(" ");//                    printf("\n");//                    printf("cnt = %d\n", cnt);//                    printf("***////\n");//                    system("pause");//                }                sort(ma, ma + 14, cmp2);            }        }        for(int i = 3 ; i < 10 ; i++){            int tnum = 0;            for(int j = 0 ; j < 13 ; j++)                if(ma[j].u == i)    tnum++;            if(tnum >= 4)   continue;            ins(i, -1);            sort(ma, ma + 14, cmp1);            if(check(i, 100)){                ans[cnt++] = M(i, -1, 0);            }            for(int k = 0 ; k < 14 ; k++)   ma[k].vis = 0;            for(int j = 0 ; j < 14 ; j++){                if(ma[j].u == i){ma[j].vis = 1; break;}            }            sort(ma, ma + 14, cmp2);        }        printf("Case %d: ", ++cas);        if(cnt == 0)    printf("Not ready\n");        else{            for(int i = 0 ; i < cnt ; i++){                output(ans[i]);                if(i < cnt - 1) printf(" ");                else    printf("\n");            }        }    }    return 0;}

例题10 UVA 11384
每次消除一半的那个数就行,看看能消除多少次。如果是奇数,则n -> (n+1)/2.
源码:

#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <iostream>#include <string>using namespace std;int main(){    int n;    while(scanf("%d", &n) != EOF){        int cnt = 0;        while(n){            if(n % 2 == 0)  n /= 2;            else    n = (n - 1) / 2;            cnt++;        }        printf("%d\n", cnt);    }    return 0;}

~例题11 UVA 10795
汉诺塔问题为背景,给出每个盘子的位置(只有三个位置,最多60个盘子),问两个局面相互转化的最少步数。
汉诺塔有一个结论,即把一摞n个盘子从一根柱子移动到另一个柱子上需要2^n-1步数。
那么对于本题,由于大盘子必须要放在小盘子上面,所以从后往前遍历,大盘子已经在目标位置上的就不用移动,换句话说,就不用管它了。
然后遇到一个不满足的最大的盘子i,要从1->2(比如),那么需要把1和2上的盘子全都移动到3上才行。
故设函数dfs(int mark, int postion, int desinatin)这时候就递归往下处理,不断更改目标点的参数即可。递归的时候思想也和移动大盘子的思想一样,详见代码。
源码:

#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <iostream>#include <string>using namespace std;int main(){    int n;    while(scanf("%d", &n) != EOF){        int cnt = 0;        while(n){            if(n % 2 == 0)  n /= 2;            else    n = (n - 1) / 2;            cnt++;        }        printf("%d\n", cnt);    }    return 0;}

例题12 UVA 12124

/*    二分容易写残,包括边界(大于等于)和单调性(每次判断后改变右边界还是左边界)的问题自己要把握一下。    这里处理得好就不用判断最后得到的品质在不在这些所有物品的品质里面了    因为它会取一个最大的满足的边界,而这个边界肯定是一个物品的品质。比这个高,就会在这个种类中选到其他物品导致价格升高,然后就非法了。    WA了几发还有惯性的没考虑到品质高价格也低的情况。*/#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <iostream>#include <string>#include <map>using namespace std;#define inf (1000000007)const int MAXN = 1000 + 5;map<string,int>name;int cnt;struct D{    int u, b;}d[MAXN][MAXN];int mmin[MAXN][MAXN];int num[MAXN];char s[25];bool cmp(D l, D r){    if(l.b == r.b)  return l.u < r.u;    else    return l.b < r.b;}int solve(int u){//    printf("u = %d\n", u);    int ans = 0;    for(int i = 1 ; i <= cnt ; i++){        int le = 0, re = num[i] - 1;        if(d[i][re].b < u)  return inf;        while(le < re - 1){            int mid = (le + re) >> 1;            if(d[i][mid].b >= u) re = mid;      ///此处要大于等于            else    le = mid;        }        int choose = 0;        if(d[i][le].b == d[i][re].b)    ans += mmin[i][le];        else if(d[i][le].b >= u && d[i][le].u <= d[i][re].u)    ans += mmin[i][le];     ///此处坑点,品质高的不一定容量大        else    ans += mmin[i][re], choose = 1;//        if(choose == 0) printf("i = %d, d[i][le].u = %d, d[i][le].b = %d\n", i, d[i][le].u, d[i][le].b);//        else    printf("i = %d,d[i][re].u = %d, d[i][re].b = %d\n", i, d[i][re].u, d[i][re].b);    }//    printf("ans = %d\n", ans);//    system("pause");//    printf("\n");    return ans;}bool have(int u){    for(int i = 1 ; i <= cnt ; i++)        for(int j = 0 ; j < num[i] ; j++)            if(d[i][j].b == u)  return true;    return false;}int main(){    int T;    scanf("%d", &T);    while(T--){        int n, b;        scanf("%d%d", &n, &b);        memset(num, 0, sizeof(num));        name.clear();        cnt = 0;        for(int i = 0 ; i < n ; i++){            scanf("%s", s);            if(name[s] == 0)  name[s] = ++cnt;            int mark = name[s];            scanf("%s%d%d", s, &d[mark][num[mark]].u, &d[mark][num[mark]].b);            num[mark]++;        }        for(int i = 1 ; i <= cnt;  i++) sort(d[i], d[i] + num[i], cmp);        for(int i = 1 ; i <= cnt ; i++){            for(int j = num[i] - 1 ; j >= 0 ; j--){                if(j == num[i] - 1) mmin[i][j] = d[i][j].u;                else mmin[i][j] = min(d[i][j].u, mmin[i][j + 1]);            }        }        int re = 1000000000 + 7, le = 0;        while(le < re - 1){            int mid = (le + re) >> 1;            int temp = solve(mid);//            printf("temp = %d, le = %d, re = %d\n", temp, le, re);//            system("pause");            if(temp <= b)   le = mid;            else    re = mid;        }        if(solve(re) <= b && have(re))  printf("%d\n", re);        else    printf("%d\n", le);    }    return 0;}

例题 13 UVA 12097
假设当前枚举的面积值是S,则每个pie最多贡献floor(AreaOfPie / S)

/*    二分入门。题目坑点是自己也要分一份,也就是总人数k需要加1*/#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <iostream>#include <string>#include <vector>using namespace std;const int MAXN = 10000 + 5;double s[MAXN];const double eps = 1e-6;const double PI = acos(-1.0);int main(){    int T;    scanf("%d", &T);    while(T--){        int n, k;        scanf("%d%d", &n, &k);        k++;        for(int i = 0 ; i < n ; i++)    scanf("%lf", &s[i]), s[i] = s[i] * s[i] * PI;        double le = 0, re = 10000 * 10000 * PI;        while(re - le >= eps){            double mid = (re + le) / 2.0;            int num = 0;            for(int i = 0 ; i < n ; i++){                num += s[i] / mid;            }//            printf("mid = %f, num = %d\n", mid, num);            if(num >= k)    le = mid;            else    re = mid;        }        printf("%.4f\n", le);    }    return 0;}

例题 14 UVA 11520

/*    回溯加强剪枝,主要是valid函数中的第三第四个特判。    其实也有代码能力的训练*/#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <iostream>#include <string>using namespace std;const int MAXN = 10 + 5;char s[MAXN][MAXN];char g[MAXN][MAXN];int n;bool valid(int i, int j, char c){    if(i > 1 && g[i - 1][j] == c) return false;    if(j > 1 && g[i][j - 1] == c) return false;    if(i < n && s[i + 1][j] != '.' && s[i + 1][j] == c) return false;   ///此处不加会TLE,下同    if(j < n && s[i][j + 1] != '.' && s[i][j + 1] == c) return false;    return true;}bool dfs(int i, int j){//    printf("i = %d, j = %d\n", i, j);//    system("pause");    int tx, ty;    tx = (i == n ? 1 : i + 1);    ty = (i == n ? j + 1 : j);    if(s[i][j] != '.'){        g[i][j] = s[i][j];        if(!valid(i, j, s[i][j]))   return false;        else if(i == n && j == n)    return true;        else return dfs(tx, ty);    }    else{        for(int k = 0 ; k < 26 ; k++){            g[i][j] = 'A' + k;            if(valid(i, j, 'A' + k)){       ///注意是i,j不是tx,ty                if(i == n && j == n)    return true;    ///没出样例时这里忘记特判陷入死循环                if(dfs(tx, ty)) return true;            }        }    }    return false;}int main(){    int T;    scanf("%d", &T);    for(int cas = 1 ; cas <= T ; cas++){        scanf("%d", &n);        for(int i = 1 ; i <= n ; i++)   scanf("%s", s[i] + 1);        dfs(1, 1);        printf("Case %d:\n", cas);        for(int i = 1 ; i <= n ; i++){            for(int j = 1 ; j <= n ; j++){                printf("%c", g[i][j]);//                printf("i = %d, j = %d\n", i, j);//                system("pause");            }            printf("\n");        }    }    return 0;}

~例题 15 UVA 1267

/*    dfs函数用于初始化图的父子关系和深度    大白书上有一句话说的好——对于已经满足条件的点,直接当他们不存在好了    用优先队列,每次弹出没有访问的叶子节点(注意这里有两个判断)。然后找到距离它距离它为k的祖先。*/#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <string>#include <algorithm>#include <iostream>#include <vector>#include <queue>using namespace std;const int MAXN = 1000 + 5;int fa[MAXN], deep[MAXN];int use[MAXN];bool leaf[MAXN];vector<int>lin[MAXN];void dfs(int u, int p){    int ok = 1;    for(int i = 0 ; i < (int)lin[u].size() ; i++){        int v = lin[u][i];        if(v == p)  continue;        deep[v] = deep[u] + 1;        fa[v] = u;        dfs(v, u);        ok = 0;    }    if(p != -1 && ok == 1)  leaf[u] = true;    else    leaf[u] = false;}struct D{    int u, v;   ///u for position, v for hightness    bool operator < (const D &rbs)const{        return v < rbs.v;    }    D(){}    D(int _u, int _v){u = _u, v = _v;}};int n, s, k;priority_queue<D>que;void del(int u, int cnt, int p){//    printf("u = %d, cnt = %d\n", u, cnt);    use[u] = 1;    if(cnt == k)    return;    for(int i = 0 ; i < (int)lin[u].size() ; i++){        int v = lin[u][i];        if(v == p)  continue;        del(v, cnt + 1, u);    }}int main(){//    freopen("UVA 1267.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--){        scanf("%d%d%d", &n, &s, &k);        int u, v;        for(int i = 1 ; i <= n ; i++)   lin[i].clear();        for(int i = 1 ; i < n ; i++){            scanf("%d%d", &u, &v);            lin[u].push_back(v);            lin[v].push_back(u);        }        deep[s] = 0;        dfs(s, -1);        while(!que.empty()) que.pop();        for(int i = 1 ; i <= n ; i++){            if(deep[i] > k) use[i] = 0, que.push(D(i, deep[i]));            else    use[i] = 1;        }        int ans = 0;        while(!que.empty()){            D temp = que.top();   que.pop();            u = temp.u;//            printf("u = %d, deep = %d\n", u, temp.v);            if(use[u] || !leaf[u])  continue;            int now = u;            int num = 0;            while(num < k){                now = fa[now];                num++;            }            del(now, 0, -1);            ans++;//            printf("u = %d, now = %d\n", u, now);//            printf("use situation ");//            for(int i = 1 ; i <= n ; i++)   printf("%d ", use[i]);//            printf("\n");//            system("pause");        }        printf("%d\n", ans);    }    return 0;}

~例题 16 UVA 1335
/*
贪心,看着题解打的。

偶数的想到了,即相邻的两个值和的最大值。具体想法举例就是奇数尽量往下面取,偶数尽量往上面取。奇数比较复杂,按照偶数的取法会出问题。那么,对于最后一个数a[n],它首先不能和第一个数a[1]的部分重合,其次不能和a[n-1]部分重合。如果a[n-1]和a[1]重合的部分最多就好了。所以思路是贪心。除了第一个外,对于偶数的尽量往前放,奇数的尽量往后放。可以看出对于i<j的a[i]这样放时,若j为偶数,a[j]与a[1]重合部分也是最多的。那么,具体实现把一个数分成两部分:与a[1]重合和不与a[1]重合的部分。
*/#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <iostream>#include <string>using namespace std;const int MAXN = 100000 + 5;int a[MAXN];int x[MAXN], y[MAXN];bool check(int n, int mid){    int up = a[1];    x[1] = a[1];    if(mid < up)    return false;    for(int i = 2 ; i <= n - 1; i++){        int rest = up - x[i - 1];        if(i % 2 == 0){            x[i] = min(rest, a[i]);            y[i] = a[i] - x[i];            if(y[i] > mid - up) return false;        }        else{            y[i] = min(a[i], mid - up - y[i - 1]);            x[i] = a[i] - y[i];            if(x[i] > up)   return false;        }    }    int rest = mid - (up + y[n - 1]);    x[n] = 0, y[n] = a[n];//    if(mid <= 10){//    printf("mid = %d, up = %d\n", mid, up);//    for(int i = 1 ; i <= n ; i++)   printf("x = %d, y = %d\n", x[i], y[i]);//    system("pause");//    }    if(a[n] > rest) return false;    return true;}int main(){    int n;    while(scanf("%d", &n) != EOF && n){        for(int i = 1 ; i <= n ; i++)   scanf("%d", &a[i]);        if(n == 1){            printf("%d\n", a[1]);            continue;        }        int ans = 0;        for(int i = 2 ; i <= n ; i++)   ans = max(ans, a[i - 1] + a[i]);        if(n % 2 == 0){            ans = a[1] + a[n];            for(int i = 2 ; i <= n ; i++)   ans = max(ans, a[i] + a[i - 1]);        }        else{            int le = 0, re =  100000 * 3 + 1;            while(le < re - 1){                int mid = (le + re) >> 1;                if(check(n, mid))   re = mid;                else    le = mid;            }            if(check(n, le))   ans = le;            else    ans = re;        }//        printf("ans = %d\n", ans);        printf("%d\n", ans);    }    return 0;}
0 0
原创粉丝点击