T47092 作业

暴力出奇迹,暴力吊打正解。。。

这道题看上去是求方案数,吓得我用dp写。但是我又不会dp,结果就爆零了。。。

于是痛心疾首的我怒打暴力回溯,一拿就拿了90pt。

最后加上了一个很小很小的小剪枝,冲到了最优解首页。。。


上面是扯淡的。

要写dp也好写啊!定义dp[i][j]为当前最后一个点是i,状态为j的方案数。

转移的话很显然就不说了。

只不过要注意dp的更新顺序。我因为顺序没弄好爆零了!!!

但是也不会注意顺序啊

于是我萌生了另一个想法:像“宝藏”那道题一样用dfs的框架来更新dp。结果现在还没过。

代码给两份。

  1. 暴力出奇迹。
#include<cstdio>
#include<algorithm>
const int maxn = 20;
const int INF = 19260817;
const int mod = 4921057;
int a[maxn], b[maxn], n;
bool vis[maxn];
int ans;
bool check(int x, int y)
{
    bool flag = false;
    if(x > y) std::swap(x, y), flag = true;
    int cnt = 0;
    for(int i = x + 1; i < y; i++) if(!vis[i]) cnt++;
    return cnt <= b[flag ? y : x];
}
void dfs(int u, int t)
{
    if(t == n) ans = (ans + 1) % mod; 
    else
    {
        for(int i = 1; i <= n; i++) if(!vis[i] && a[u] > a[i]) return;// 很弱智的剪枝
        for(int i = 1; i <= n; i++)
        {
            if(!vis[i] && a[u] <= a[i] && check(u, i))
            {
                vis[i] = true;
                dfs(i, t + 1);
                vis[i] = false;
            }
        }
    }
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
    int minv = INF;
    for(int i = 1; i <= n; i++) minv = std::min(a[i], minv);
    for(int i = 1; i <= n; i++)
    {
        if(a[i] == minv)
        {
            vis[i] = true;
            dfs(i, 1);
            vis[i] = false;
        }
    }
    printf("%d\n", ans);
    return 0;
}
  1. 递推更新的状压dp。
#include<cstdio>
#include<algorithm>

// 状态这么定义:
// 定义dp[i][j]为当前最后一个作业为i,状态为j的方案数

const int maxn = 19, maxN = 262205;
const int INF = 0x3f3f3f3f, mod = 4921057;

int a[maxn], b[maxn], n;
int dp[maxn][maxN];

bool check(int i, int j, int status)
{
    bool flag = false;
    if(i > j) std::swap(i, j), flag = true;
    int cnt = 0;
    for(int k = i + 1; k < j; k++)
    {
        if(!(status & (1 << (k - 1)))) cnt++;
    }
    return cnt <= b[flag ? j : i];
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
    int S = (1 << n);
    int amin = INF;
    for(int i = 1; i <= n; i++) amin = std::min(amin, a[i]);
    for(int i = 1; i <= n; i++)
    {
        // if(amin == a[i]) dp[i][1 << (i - 1)] = 1;
        dp[i][1 << (i - 1)] = 1;
    }
    for(int j = 1; j < S; j++)// 当前状态
    {
        for(int i = 1; i <= n; i++)// 当前最后一个点。i和j这里要这么写,不然爆零
        {
            if((1 << (i - 1)) & j)
            {
                for(int k = 1; k <= n; k++)// 新点
                {
                    if((1 << (k - 1)) & j) continue;
                    if(a[i] <= a[k] && check(i, k, j))
                    {
                        dp[k][j | (1 << (k - 1))] = (dp[k][j | (1 << (k - 1))] + dp[i][j]) % mod;
                    }
                }
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= n; i++) ans = (ans + dp[i][S - 1]) % mod;
    printf("%d\n", ans);
    return 0;
}
posted @ 2018-10-04 18:04  Garen-Wang  阅读(105)  评论(0编辑  收藏  举报