HDU5900 QSC and Master(区间DP + 最小费用最大流)
题目
Source
http://acm.hdu.edu.cn/showproblem.php?pid=5900
Description
Every school has some legends, Northeastern University is the same.
Enter from the north gate of Northeastern University,You are facing the main building of Northeastern University.Ninety-nine percent of the students have not been there,It is said that there is a monster in it.
QSCI am a curious NEU_ACMer,This is the story he told us.
It’s a certain period,QSCI am in a dark night, secretly sneaked into the East Building,hope to see the master.After a serious search,He finally saw the little master in a dark corner. The master said:
“You and I, we're interfacing.please solve my little puzzle!
There are N pairs of numbers,Each pair consists of a key and a value,Now you need to move out some of the pairs to get the score.You can move out two continuous pairs,if and only if their keys are non coprime(their gcd is not one).The final score you get is the sum of all pair’s value which be moved out. May I ask how many points you can get the most?
The answer you give is directly related to your final exam results~The young man~”
QSC is very sad when he told the story,He failed his linear algebra that year because he didn't work out the puzzle.
Could you solve this puzzle?
(Data range:1<=N<=300
1<=Ai.key<=1,000,000,000
0<Ai.value<=1,000,000,000)
Input
First line contains a integer T,means there are T(1≤T≤10) test case。
Each test case start with one integer N . Next line contains N integers,means Ai.key.Next line contains N integers,means Ai.value.
Output
For each test case,output the max score you could get in a line.
Sample Input
3
3
1 2 3
1 1 1
3
1 2 4
1 1 1
4
1 3 4 3
1 1 1 1
Sample Output
0
2
0
分析
题目大概说给N个<key,value>二元组,每次可以取出相邻的且其key的GCD不为1的两个二元组,并获得二者value之和的价值,问能取到的最大价值是多少?
注意取掉后就左右两部分就合并在一起。。
显然考虑区间DP:
- dp[i][j]表示下标i到j的二元组全部消除能获得的最大价值
- 通过枚举k(i<=k<j)从max(dp[i][k]+dp[k+1][j])转移;此外如果key[i]等于key[j],还能从dp[i+1][j-1]+value[i]+value[j]转移
于是这样就能求出所有能消除的全部区间以及消除它们能获得的最大价值,而问题就是要从这些区间取出不重叠的几个使得价值和最大。
这个可以用最小费用最大流解,区间k覆盖。。也能再dp一次,dp[i]表示考虑坐标点在i之前的区间所能获得的最大价值。。
代码
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; #define INF (1LL<<60) #define MAXN 333 #define MAXM 3333*666 struct Edge{ int u,v,next; long long cap,cost; }edge[MAXM]; int head[MAXN]; int NV,NE,vs,vt; void addEdge(int u,int v,long long cap,long long cost){ edge[NE].u=u; edge[NE].v=v; edge[NE].cap=cap; edge[NE].cost=cost; edge[NE].next=head[u]; head[u]=NE++; edge[NE].u=v; edge[NE].v=u; edge[NE].cap=0; edge[NE].cost=-cost; edge[NE].next=head[v]; head[v]=NE++; } bool vis[MAXN]; long long dis[MAXN]; int pre[MAXN]; bool SPFA(){ for(int i=0; i<NV; ++i){ vis[i]=0; dis[i]=INF; } vis[vs]=1; dis[vs]=0; queue<int> que; que.push(vs); while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u]; i!=-1; i=edge[i].next){ int v=edge[i].v; if(edge[i].cap && dis[v]>dis[u]+edge[i].cost){ dis[v]=dis[u]+edge[i].cost; pre[v]=i; if(!vis[v]){ vis[v]=1; que.push(v); } } } vis[u]=0; } return dis[vt]!=INF; } long long MCMF(){ long long res=0; while(SPFA()){ long long flow=INF,cost=0; for(int u=vt; u!=vs; u=edge[pre[u]].u){ flow=min(flow,edge[pre[u]].cap); } for(int u=vt; u!=vs; u=edge[pre[u]].u){ edge[pre[u]].cap-=flow; edge[pre[u]^1].cap+=flow; cost+=flow*edge[pre[u]].cost; } res+=cost; } return res; } long long d[MAXN][MAXN]; long long gcd(long long a,long long b){ if(b==0) return a; return gcd(b,a%b); } bool ok[MAXN][MAXN]; long long key[MAXN],val[MAXN]; int main(){ int t,n; scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1; i<=n; ++i){ scanf("%I64d",key+i); } for(int i=1; i<=n; ++i){ scanf("%I64d",val+i); } memset(ok,0,sizeof(ok)); for(int i=1; i<=n; ++i){ for(int j=i+1; j<=n; ++j){ if(gcd(key[i],key[j])==1) continue; ok[i][j]=1; ok[j][i]=1; } } for(int i=1; i<=n; ++i){ for(int j=1; j<=n; ++j){ d[i][j]=-INF; } } for(int len=2; len<=n; ++len){ for(int i=1; i+len-1<=n; ++i){ if(ok[i][i+len-1] && len==2){ d[i][i+len-1]=val[i]+val[i+len-1]; continue; } if(ok[i][i+len-1]) d[i][i+len-1]=max(d[i][i+len-1],val[i]+val[i+len-1]+d[i+1][i+len-1-1]); for(int j=0; j<len-1; ++j){ d[i][i+len-1]=max(d[i][i+len-1],d[i][i+j]+d[i+j+1][i+len-1]); } } } long long ans=0; vs=0; vt=n+2; NV=vt+1; NE=0; memset(head,-1,sizeof(head)); addEdge(vs,1,1,0); addEdge(n+1,vt,1,0); for(int i=1; i<=n; ++i){ addEdge(i,i+1,INF,0); } for(int i=1; i<=n; ++i){ for(int j=i+1; j<=n; ++j){ if(d[i][j]!=-INF){ addEdge(i,j+1,1,-d[i][j]); } } } printf("%I64d\n",-MCMF()); } }