题意:
n个点 m条有向边 选恰好k个点
下面n个数给出点权
下面m行给出边和边权
设上一次选的点是u这一次选的点是v,则可以获得边权u->v的价值。
问:使得选出的权和最大,问最大的权和
思路:显然是状压dp,每个点只有两种情况,选或者不选,
dp[i][j]表示选的点状态为i,最后一次选的点是j的最大价值。
状态转移方程:dp[(1 << k) ^ i][k + 1] = max(dp[i][t + 1] + val[t + 1][k + 1] + a[k], dp[(1 << k) ^ i][k + 1])
代码如下:
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1 << 19;
int n, m, k;
int a[20], val[20][20];
ll dp[N][20]; //dp[i][j] 状态为i,最后一个吃掉的是j的最大愉悦度
int main()
{
while(~scanf("%d%d%d", &n, &m, &k))
{
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
memset(val, 0, sizeof(val));
while(k--)
{
int x, y, c;
scanf("%d%d%d", &x, &y, &c);
val[x][y] = c;
}
ll ans = 0;
for(int i = 0; i < n; i++)
{
dp[1<<i][i+1] = a[i];
ans = max(ans, (ll)(a[i]));
}
for(int i = 0; i < (1 << n); i++)
{
int tmp[20], sz = 0;
for(int j = 0; j < n; j++)
{
if((1 << j) & i) tmp[sz++] = j;
}
if(sz >= m) continue;
for(int k = 0; k < n; k++)
{
if(((1 << k) & i) == 0)
{
for(int j = 0; j < sz; j++)
{
int t = tmp[j];
dp[(1 << k) ^ i][k + 1] = max(dp[i][t + 1] + val[t + 1][k + 1] + a[k], dp[(1 << k) ^ i][k + 1]);
if(sz == m-1)
{
ans = max(dp[(1 << k) ^ i][k + 1], ans);
}
}
}
}
}
printf("%I64d\n", ans);
}
return 0;
}