前言:没有前言.
T1 Hie with the Pie
题意:
给你几个点,每个点都有到其他点的价值,请问遍历所有点的最小价值.
解析:
最短路+状压.
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int lu[20][20], dp1[20][20], dp2[1 << 11][20];
int main()
{
int n, i, j, k;
while (~scanf("%d", &n), n)
{
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
{
scanf("%d", &lu[i][j]);
dp1[i][j] = lu[i][j];
}
for (int j = 0; j <= n; j++)
for (int i = 0; i <= n; i++)
for (int k = 0; k <= n; k++)
dp1[i][j] = min(dp1[i][k] + lu[k][j], dp1[i][j]);
memset(dp2, -1, sizeof(dp2));
dp2[1][0] = 0;
for (int i = 1; i < 1 << (n + 1); i++)
{
i = i | 1;
for (int j = 0; j <= n; j++)
if (dp2[i][j] != -1)
for (int k = 0; k <= n; k++)
if (j != k && (dp2[(1 << k) | i][k] == -1 || dp2[(1 << k) | i][k] > dp2[i][j] + dp1[j][k]))
dp2[(1 << k) | i][k] = dp2[i][j] + dp1[j][k];
}
printf("%d\n", dp2[(1 << (n + 1)) - 1][0]);
}
return 0;
}
提示:
可以预处理出路径的价值.
出处:
T2 Doing Homework
题意:
给你几门科目,给定必须做完的时间和做好需要的时间.,问最小超时时的做作业顺序.(字典序).
解析:
标准的状压dp模板.
代码:
#include <bits/stdc++.h>
using namespace std;
const int inf = 1 << 30;
struct node
{
string name;
int shi, jie;
} a[50];
struct kode
{
int time, score, pre, now;
} dp[1 << 15];
int main()
{
int t, i, j, s, n, end;
cin >> t;
while (t--)
{
memset(dp, 0, sizeof(dp));
cin >> n;
for (i = 0; i < n; i++)
cin >> a[i].name >> a[i].shi >> a[i].jie;
end = 1 << n;
for (s = 1; s < end; s++)
{
dp[s].score = inf;
for (i = n - 1; i >= 0; i--)
{
int pp = 1 << i;
if (s & pp)
{
int past = s - pp;
int st = dp[past].time + a[i].jie - a[i].shi;
if (st < 0)
st = 0;
if (st + dp[past].score < dp[s].score)
{
dp[s].score = st + dp[past].score;
dp[s].now = i;
dp[s].pre = past;
dp[s].time = dp[past].time + a[i].jie;
}
}
}
}
stack<int> S;
int pp = end - 1;
cout << dp[pp].score << endl;
while (pp)
{
S.push(dp[pp].now);
pp = dp[pp].pre;
}
while (!S.empty())
{
cout << a[S.top()].name << endl;
S.pop();
}
}
return 0;
}
提示:
可以把每个状态变成二进制来表示.
出处:
T3 Card Collector
题意:
给你几个卡片出现的概率,问收集所有卡的最小需要包数.
解析:
用状压表示状态.
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 21;
double nn[maxn];
double dp[1 << maxn];
int main()
{
int n;
while (scanf("%d", &n) != EOF)
{
double zz = 0;
for (int i = 0; i < n; i++)
{
scanf("%lf", &nn[i]);
zz += nn[i];
}
dp[(1 << n) - 1] = 0;
zz = 1 - zz;
for (int i = (1 << n) - 2; i >= 0; i--)
{
double x = 0, mm = 1;
for (int j = 0; j < n; j++)
{
if ((i & (1 << j)))
x += nn[j];
else
mm += nn[j] * dp[i | (1 << j)];
}
dp[i] = mm / (1 - zz - x);
}
printf("%.5lf\n", dp[0]);
}
return 0;
}
提示:
非常迷的计算方法.