这题我一直以为是贪心做的,做了好久找不到规律
看了解题报告,学习了压缩dp,类似dist,{v(i)}=min{v(i)-k},这种NP问题,O(n*2^n),数据给的比较小,其实也只能给15了,直接用2<<15数组表示状态
每个状态,记录最优的pre,now,当然pre(上一个状态量)和now(当前节点值)是有关的,其实可以记录一个,最后递归输出now,有几个重要的过程注释在代码里了
哎,最坑的是,解题报告看那什么,直接记住代码了,最后还记错了=.=,不过还好,debug的时候,算是更加了解过程了,不说了,这周可以恶刷dist了,其实koubin的算法更加类似dist,这个代码和之前的dp接近
//状态压缩,用1111表示4中的取值情况,0000表示都不取,0001表示只取1 //这里dp判断还是比较简单的,类似最短路径的,{V{i}} = min{V{i}-k}(k属于i中的集合) //这里路径小于0,这条路径损失的分数小于0,作0处理 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <stack> #include <string> using namespace std; const int MAXN = (1<<31)-1; typedef struct{ string cn; int tm; int del; }course; typedef struct{ int pre; int now; int tm; int red; }node; course a[16]; node dp[1<<15]; void init(){ dp[0].pre = -1; dp[0].now = -1; dp[0].red = 0; dp[0].tm = 0; } int main() { int t; scanf("%d",&t); while(t--){ int n; scanf("%d",&n); for(int i=0;i<n;i++){ cin>>a[i].cn; scanf("%d%d",&a[i].del,&a[i].tm); } init(); int tem = (1<<n)-1; for(int i=1;i<=tem;i++){ dp[i].red = MAXN; for(int j=n-1;j>=0;j--){ //因为是按输入顺序字典序增加的,前优先,则前优先;比如0101->->1111,如果0101->1101->1111与0101->0111->1111代价相同,则A优先B int s = 1<<j; //这里我们判断离1111最近的取值,因为A比D先取了,所以D才是最靠近1111,则先取后段才是我们要求的前优先 if(i&s){ int past = i-s; int red = dp[past].tm+a[j].tm-a[j].del; if(red<0)red = 0; if(dp[i].red>dp[past].red+red){ dp[i].red = dp[past].red+red; dp[i].now = j; dp[i].pre = past; dp[i].tm = dp[past].tm+a[j].tm; } } } } cout<<dp[tem].red<<endl; stack <int >s1; while(tem){ s1.push(dp[tem].now); tem = dp[tem].pre; } while(!s1.empty()){ int tx = s1.top(); s1.pop(); cout<<a[tx].cn<<endl; } // cout<<"debug"<<endl; } //cout << "Hello world!" << endl; return 0; }
时间: 2024-10-13 02:26:19