T47092 作业
暴力出奇迹,暴力吊打正解。。。
这道题看上去是求方案数,吓得我用dp写。但是我又不会dp,结果就爆零了。。。
于是痛心疾首的我怒打暴力回溯,一拿就拿了90pt。
最后加上了一个很小很小的小剪枝,冲到了最优解首页。。。
上面是扯淡的。
要写dp也好写啊!定义dp[i][j]为当前最后一个点是i,状态为j的方案数。
转移的话很显然就不说了。
只不过要注意dp的更新顺序。我因为顺序没弄好爆零了!!!
但是也不会注意顺序啊
于是我萌生了另一个想法:像“宝藏”那道题一样用dfs的框架来更新dp。结果现在还没过。
代码给两份。
- 暴力出奇迹。
#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;
}
- 递推更新的状压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;
}