链接
题意:
给出N个节点,给出M条边,我们只能从低节点跳到高节点。然后给出N个节点属于的公司,给出第i个公司第一次到给多少奖金。问从节点1开始,分别到达节点(1~n)最大奖金。
分析:
其实不难想到我们如果当前节点在x,有(a1,a2,a3…)这些节点可以到达节点x,那么我们选取(a1,a2,a3…)所有的状态转移过来就好了,状态表示我们可以用二进制,也可以用字符串数组等等,只要可以表示状态就行。
状态转移有两种状态
- 一种是在之前出现过,不用再加奖金了,把这个状态加入x
- 另一种之前没有出现过,加上奖金并将状态改变进入x.
当然这样是会超时的!!!!
所以我们需要优化,如何优化那?
如果连线如下:
也就是
1 2
2 3
3 4
1 4
2 4
那么当我们计算节点4时,我们会跑节点(1,2,3),我们想有什么可以优化的吗?
当然有呀!!!!
计算节点3时我们会跑节点(2),
而计算节点2时我们会跑节点(1),
这样看来,当我们计算节点4时,我们跑了节点3就意味着我们已经跑了节点2,跑了节点2也就意味着跑了节点1,这样我们只需要跑 一个节点3就好了。
是不是很妙,但就是这么妙, 复杂度嘛,
如果算上我优化的思路:
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
for(int k=i+1;k<j;k++){
if(dp[i][j]==1&&dp[i][k]==1&&dp[k][j]==1){
dp[i][j]=0;
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(dp[i][j]==1){
vv[j].push_back(i);
}
}
}
这个就是
O
(
n
3
)
O(n^3)
O(n3)
然后下面查询节点就是
O
(
n
)
O(n)
O(n)的。
因为经过上面优化他的前驱节点就只有一个,而且每个节点前驱节点都只有一个那么状态也只有一个,那么就是
O
(
n
)
O(n)
O(n)的复杂度呗
所以最终复杂度就是
O
(
n
3
+
n
)
O(n^3+n)
O(n3+n)
当然可能还可以把
O
(
n
3
)
O(n^3)
O(n3)这个优化下。但是对于这个题大可不必。
所以当我们遇到问题时换 个角度想可能就会发现新大陆!!!
ll n, m;
ll a[40],b[40];
vector<pii> v[40];
vector<ll> vv[40];
string str;
ll ans[50];
ll dp[50][50];
void solve()
{
cin >> n >> m;
for(int i=1;i<=n;i++) cin>>a[i],str+="0";
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=m;i++){
ll x,y;
cin>> x>>y;
dp[x][y]=1;
//vv[y].push_back(x);
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
for(int k=i+1;k<j;k++){
if(dp[i][j]==1&&dp[i][k]==1&&dp[k][j]==1){
dp[i][j]=0;
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(dp[i][j]==1){
vv[j].push_back(i);
}
}
}
ans[1]=b[a[1]];
ll res=ans[1];
string tmp=str;
tmp[a[1]-1]='1';
v[1].push_back({res,tmp});
for(int i=2;i<=n;i++){
ll sum=0;
for(int j=0;j<vv[i].size();j++){
ll pre=vv[i][j];/// pre
for(int k=0;k<v[pre].size();k++){
pii top = v[pre][k];
res=top.x;
tmp=top.y;
if(tmp[a[i]-1]=='0'){
tmp[a[i]-1]='1';
res+=b[a[i]];
}
v[i].push_back({res,tmp});
sum=max(sum,res);
}
}
ans[i]=sum;
}
for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
}
还有一种在提交区发现的:剪枝操作,他是剪去一部分重复的状态就比如:
节点x有状态(1101,1100,1010,1110)那么(1101涵盖了1100那么1100就会被剪去,因为他比较大)
这个剪枝,剪不全,但是也会剪掉很多。
@srly
看起去瞅瞅大佬写的
#include <cstdio>
#include <vector>
#include <iostream>
#include <unordered_map>
using namespace std;
typedef long long ldkyyds;
const int maxn = 40;
int c[maxn], w[maxn];
unordered_map<ldkyyds, int> dp[maxn];
vector<ldkyyds> dis[maxn];
vector<int> vis[maxn];
int maxx[maxn];
void add(int i, ldkyyds ldknb, int val) {/// 位次 , 状态, 奖金
if (!dp[i].count(ldknb)) {
dis[i].push_back(ldknb);
}
dp[i][ldknb] = max(dp[i][ldknb], val);
maxx[i] = max(maxx[i], val);
}
void remove(int pos) {
vector<ldkyyds> diss;
for (int i = 0; i < dis[pos].size(); i ++) {
bool flag = false;
for (int j = 0; j < dis[pos].size(); j ++) {
if (j == i) {
continue;
}
if ((dis[pos][i] | dis[pos][j]) == dis[pos][j]) {/// 涵盖 i
flag = true;
break;
}
}
if (!flag) {///没有被涵盖
diss.push_back(dis[pos][i]); ///加进去
}
}
dis[pos] = diss;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> c[i];
}
for (int i = 1; i <= n; i ++) {
cin >> w[i];
}
while (m --) {
int u, v;
cin >> u >> v;
vis[v].push_back(u);
}
add(1, 1ll<<c[1], w[c[1]]);
for (int i = 2; i <= n; i ++) {
for (int j = 0; j < vis[i].size(); j ++) {
int pos = vis[i][j];///前驱
for (int t = 0; t < dis[pos].size(); t ++) {///前驱的状态
if (dis[pos][t] & 1ll<<c[i]) {///有
add(i, dis[pos][t], dp[pos][dis[pos][t]]);
} else {///没有
add(i, dis[pos][t] | 1ll<<c[i], dp[pos][dis[pos][t]] + w[c[i]]);
}
}
}
remove(i);///删除?
}
for (int i = 1; i <= n; i ++) {
cout << maxx[i] << endl;
}
return 0;
}