題意:
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;
}