1. 程式人生 > >2018ICPC南京網路賽 E

2018ICPC南京網路賽 E

題型:動態規劃(狀壓DP)  動態規劃夠噁心吧,狀態壓縮動態規劃瞭解一下?

其實題目的大致思想還是不變的:最優子集,狀態轉移,邊界條件

狀壓DP其實就是將每種狀態轉化為二進位制計法,所以在學習狀壓DP時首先得把位操作整明白了

舉幾個常用的:<< : 左移   >>  : 右移     & : 且    |  : 或     ^:異或    ~:取反

&:同為1 取1    否則取0

|: 同為0  取0   否則取1

^:相同取0   不同取1   (取反之後就是  相同取1  不同取0了啊)

題目大意:做題,怎麼做得分最多 ,但是做一道題前必須某些固定的題目唄做完,經過相應的計算公式得到你的最終得分。

題目分析:第一反應:貪心 第二反應:貪心不對;  

DP陣列:dp[i]  就表示狀態為 i  的時候的最高得分  ,i   表示已經做過的題目  ,用二進位制表示。

舉個栗子:當i=3時 ,二進位制為11,就表示做完了1 ,2兩題   所以說 ,如果有五道題,那麼所有的狀態該是多少?

是不是 11111 (1<<(5-1));

狀態轉移方程: dp[i]=max(dp[i],dp[i^(1<< j-1)]+a*c+b)   c表示第幾個做的該題(詳見程式碼)

臨界當然就是dp為0了;

上程式碼:

#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=25;
int a[maxn],b[maxn];
vector<int> pre[maxn];
int dp[1<<20];
int main()
{
	int ans=0;;
	cin>>n;
	for(int i=1;i<=n;i++){
		int si,temp;
		cin>>a[i]>>b[i]>>si;
		while(si--){
			cin>>temp;
			pre[i].push_back(temp);
		} 
	}
	memset(dp,0,sizeof(dp));
	for (int i=0;i<(1<<n);i++){
		int  flag=0;
		for(int j=1;j<=n;j++){
			if(!(i&(1<< j-1))) continue;
			for (int k=0;k<pre[j].size();k++){
				if(!(i & (1<< pre[j][k]-1))) {
					flag=1;
					break;
				}
			}
			if(flag) break;
		}
		
		if(flag) continue;
		
		for(int j=1;j<=n;j++){
			if(!(i&(1<< j-1))) continue;
			int c=0;
			int s=i;
			while(s){
				if(s&1) c++;
				s>>=1;
			}
			dp[i]=max(dp[i],dp[i^(1<< j-1)]+a[j]*c+b[j]);
			ans=max(ans,dp[i]);
		}
	}
	cout<<ans<<endl	;
 } 

仔細讀應該還是不難理解的,具體細節或者你有想hack的資料可以試一下,觀察一下細節。

歡迎加我微信鴨!