Doing Homework HDU - 1074(狀壓dp)

題目鏈接:https://vjudge.net/problem/HDU-1074
n<=15,很明顯是狀壓dp,令f[i][j]表示在狀態i下最後一位是j的最小代價(狀態i的第j位一定爲1)f[i][j]一定是從狀態(i-(1<<(k-1)))中轉移過來的,至於上一個狀態以誰結尾是最小值還需要判斷,我是在更新當前狀態時就判斷一下那個最小,因爲點的標號從1開始,那下標0的數組就沒有用到就用它來保存該狀態下的最小值,計算最小代價,你首先需要知道完成當前狀態需要花費的時間才能計算代價,所以又多開一維,f[i][j][0]表示完成當前狀態的時間(這個與順序是無關的)
f[i][j][1]還是最小代價

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=16;
int t,n,m,a[N],b[N],ans;
map<int,string> mp; 
vector<int> res;
int f[1<<N][N][2];
void dfs(int s)
{
	if(!s) return ;
	int l=0x3f3f3f3f,r;
	for(int i=n;i;i--)//找當前狀態下的最小值,輸出要求字典序最小,從後向前推的,相同代價下使後面的數大因爲是一個全排列自然整體的字典序就小了
	{
		if(f[s][i][1]<l)
		{
			l=f[s][i][1];
			r=i;
		}
	}
	res.push_back(r);
	dfs(s-(1<<(r-1)));
}
int main()
{
	cin>>t;
	while(t--)
	{
		memset(f,0x3f,sizeof f);
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			string s;
			cin>>s>>a[i]>>b[i];
			mp[i]=s;
		}
		f[0][0][0]=f[0][0][1]=0;
		for(int i=1;i<(1<<n);i++)
		{
			int l=-1,r;//l當前狀態最小代價的那個點,r最小代價
			for(int k=1;k<=n;k++)
			{
				if((i&(1<<(k-1)))&&f[i-(1<<(k-1))][0][0]!=0x3f3f3f3f)
				{
					int v=f[i-(1<<(k-1))][0][0];//前一個狀態最小代價的那個點
					f[i][k][0]=f[i-(1<<(k-1))][v][0]+b[k];//當前狀態的完成時間
					f[i][k][1]=min(f[i][k][1],f[i-(1<<(k-1))][v][1]+max(0,f[i][k][0]-a[k]));//最小代價=前幾個點的最小代價之和加當前點的代價
					if(l==-1||f[i][k][1]<r) //更新當前狀態最小代價
					{
						l=k;
						r=f[i][k][1];
					}
				}
			}
			if(l!=-1)//保存當前狀態最小代價,l=-1就是當前狀態行不通,貌似沒用到
				f[i][0][0]=l;
		}
		dfs((1<<n)-1);//遞推求順序
		cout<<f[(1<<n)-1][res[0]][1]<<endl;
		for(int i=n-1;i>=0;i--)
			cout<<mp[res[i]]<<endl;
		res.clear();
	 } 
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章