Uva 307木棍、387谜题、10160服务站总结

本文所讨论的几个问题均为NP完全问题,所以自然解决的办法就是智能穷举,设计算法时为穷举选择合适的剪枝是在短时间内解决问题的最佳选择。

1.木棍问题

#include <cstring>
#include <algorithm>
#include <cstdio>
 using namespace std;
 #define maxn 65
  int n, sum, goal;
  int stick[maxn];
 bool visit[maxn];
 
 bool cmp(const int &a, const int &b) {
     return a > b;
 }
 
 bool dfs(int now, int index, int cnt) {
     if(goal * cnt == sum) return true;//所有木棍拼凑成功

	 if(stick[n-1]>goal-now)return false;//当前所剩的最短木棍的长度都大于现阶段拼凑原木棍剩下的长度,无需继续递归

     for(int i = index; i < n; i++) {//由于对木棍按长度排序了,所以i的初值是index而不是0,我们才选择小木棍时,无需考虑大木棍,排序确保了大木棍在之前处理了。
         if(visit[i] || (i && !visit[i-1] && stick[i] == stick[i-1])) continue;//两个木棍长度一样,那么使用前一个无法成功,自然使用这个也无法成功。
         if(now + stick[i] == goal) {
             visit[i] = true;
             if(dfs(0, 0, cnt + 1)) return true;
             visit[i] = false;
//若使用此木棍最后拼成了当前原木棍,但是,后面的原木棍无法拼凑成功,则无需继续递归。
//举例说明:若此木块长为x,你可以从后面选取总长为x的木块若干,再次拼凑出该原木棍。但是,依然无法利用此木棍长度为x拼凑成功后面的原木棍。
             return false;
         } else if(now + stick[i] < goal) {
             visit[i] = true;
             if(dfs(now + stick[i], i + 1, cnt)) return true;
             visit[i] = false;
             if(now == 0) return false;//对于拼凑当前“全新”的原木棍,若使用木棍无法拼凑成功,后面任一“全新”原木棍都不能含它,题意要求使用所有木棍,无需继续递归。
         }
     }
     return false;
 }
 
 int solve() {
     sort(stick, stick + n, cmp);
     for(goal = stick[0]; goal < sum; goal++) {
         if(sum % goal != 0) continue;
         memset(visit, false, sizeof(visit));
         if(dfs(0, 0, 0)) break;
     }
     return goal;
 }
 
 int main() {
     while(~scanf("%d", &n)) {
         if(!n) break;
         sum = 0;
         for(int i = 0; i < n; i++) {
             scanf("%d", &stick[i]);
             sum += stick[i];
         }
         printf("%d\n", solve());
     }
     return 0;
 }

以上就是解决这个问题的源代码,uva:runtime 0.193s ranking 46

2.387谜题

对于这个问题,请参考精确覆盖问题,利用Dancing Link优化。

#include<cstdio>
#include<cstring>
#define MAXM 10
#define MAXN 100000
#define MAXL 110
#define INF 0x7FFFFFFF
struct node {
    int h, l;
    char s[MAXM][MAXM];
};
struct answer {
    int pos, h, l;
};
answer ans[MAXL];
node sq[MAXM];
bool vis[MAXL];
int L[MAXN], R[MAXN], U[MAXN], D[MAXN];//上下左右
int H[MAXN], S[MAXN], C[MAXN], X[MAXN];
int size;
char a[MAXM][MAXM];
void Init(int m) {
    int i;
    memset(vis, false, sizeof(vis));
    for (i = 0; i <= m; i++) {
        L[i + 1] = i;
        R[i] = i + 1;
        U[i] = D[i] = i;
        S[i] = 0;
    }
    R[m] = 0;
    size = m + 1;
}
void Link(int r, int c) {
    U[size] = c;
    D[size] = D[c];
    U[D[c]] = size;
    D[c] = size;
    if (H[r] < 0)
        H[r] = L[size] = R[size] = size;
    else {
        L[size] = H[r];
        R[size] = R[H[r]];
        L[R[H[r]]] = size;
        R[H[r]] = size;
    }
    S[c]++;
    X[size] = r;
    C[size++] = c;
}
void Remove(int c) {//缩减双向十字链表
    int i, j;
    L[R[c]] = L[c];
    R[L[c]] = R[c];
    for (i = D[c]; i != c; i = D[i]) {
        for (j = R[i]; j != i; j = R[j]) {
            U[D[j]] = U[j];
            D[U[j]] = D[j];
            S[C[j]]--;
        }
    }
}
void Resume(int c) {//恢复双向十字链表
    int i, j;
    L[R[c]] = R[L[c]] = c;
    for (i = D[c]; i != c; i = D[i]) {
        for (j = R[i]; j != i; j = R[j]) {
            U[D[j]] = D[U[j]] = j;
            S[C[j]]++;
        }
    }
}
bool Dance() {
    if (R[0] == 0)
        return true;
    int i, j, temp, c;
    for (temp = INF,i = R[0]; i; i = R[i]) {
        if (temp > S[i]) {
            temp = S[i];
            c = i;
        }
    }
    Remove(c);
    for (i = D[c]; i != c; i = D[i]) {
        vis[X[i]] = true;
        for (j = R[i]; j != i; j = R[j])
            Remove(C[j]);
        if (Dance())
            return true;
        for (j = L[i]; j != i; j = L[j])
            Resume(C[j]);
        vis[X[i]] = false;
    }
    Resume(c);
    return false;
}
void Build(int n) {
    int i, j, k, t, p, r;
    for (i = r = 0; i < n; i++) {
        for (j = 0; j <= 4 - sq[i].h; j++) {
            for (k = 0; k <= 4 - sq[i].l; k++) {
                H[++r] = -1;
                Link(r, 16 + i + 1);
                ans[r].pos = i;
                ans[r].h = j;
                ans[r].l = k;
                for (t = 0; t < sq[i].h; t++) {
                    for (p = 0; p < sq[i].l; p++) {
                        if (sq[i].s[t][p] != '0')
                            Link(r, 4 * (j + t) + k + p + 1);
                    }
                }
            }
        }
    }
}
int main() {
	//freopen("C:\\Users\\Ron\\Desktop\\sample.txt","r",stdin);
    int n, i, j, k;
    bool flag = true;
    while (scanf("%d", &n), n) {
        if (flag)
            flag = false;
        else
            putchar('\n');
        Init(16 + n);
        for (i = 0; i < n; i++) {
            scanf("%d%d", &sq[i].h, &sq[i].l);
            for (j = 0; j < sq[i].h; j++) {
                for (k = 0; k < sq[i].l; k++)
                    scanf(" %c", &sq[i].s[j][k]);
            }
        }
        Build(n);
        if (Dance()) {
            for (i = 1; i < MAXL; i++) {
                if (vis[i]) {
                    for (j = 0; j < sq[ans[i].pos].h; j++) {
                        for (k = 0; k < sq[ans[i].pos].l; k++) {
                            if (sq[ans[i].pos].s[j][k] != '0')
                                a[j + ans[i].h][k + ans[i].l] = '1'
                                        + ans[i].pos;
                        }
                    }
                }
            }
            for (i = 0; i < 4; i++) {
                for (j = 0; j < 4; j++)
                    putchar(a[i][j]);
                putchar('\n');
            }
        } else
            puts("No solution possible");
    }
	while(true);
    return 0;
}
此为387谜题问题,dancing link源代码,因为我自己看的也是云山雾绕也没加注释。 Uva:runtime 0.089s ranking 67。有兴趣的朋友可以深入研究精确覆盖问题。

本人没有使用此法,就是利用简单的穷举加回溯得到了Uva AC。不过时间多了很多2.2s。

3.服务站

请参考寂静山林博客 ,想尽各种办法,如果不对地图进行拆分,基本上都是TLE。本来想要转载此文,并进行进一步说明,心想也没有太大必要,原文也很不错。

博客里有源码:Uva: runtime 0.029s ranking 70。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值