标签:return 完成 txt 思路 article 字典序 csdn 分析 sdn
[https://vjudge.net/contest/256508#problem/D]
就是有n门课,名称,截止日期,完成需要的时间
一旦超过截止日期一天就扣一分
问怎样安排做的顺序使得扣分最少并输出做的顺序
有相同扣分的输出字典序最小那个
我是刚学状压dp,想了半天不知道怎么做就看了题解
还是学到了很多技巧,还是需要多做才有套路
很难想到的对于我来说这题
参考的思路
[https://blog.csdn.net/libin56842/article/details/24316493]
某个状态是可以有很多状态转移而来的,
思路:因为最多只有15门课程,可以使用二进制来表示所有完成的状况
例如5,二进制位101,代表第一门和第三门完成了,第二门没有完成,那么我们可以枚举1~1<<n便可以得出所有的状态
然后对于每一门而言,其状态是t = 1<<i,我们看这门在现在的状态s下是不是完成,可以通过判断s&t是否为1来得到
当得出t属于s状态的时候,我们便可以进行DP了,在DP的时候要记录路径,方便之后的输出
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<string>
using namespace std;
const int inf=0x3f3f3f3f;
struct str{
string s;
int d,c;
};
str a[20];
struct node{
int endtime,last,cur,score;
//该状态结束的时间,以及上一个状态序号一会儿方便输出,当前的课程序号,当前状态被扣的分
}dp[1<<15];
int t,n;
int main(){
//freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=0;i<n;i++)
cin>>a[i].s>>a[i].d>>a[i].c;
vector<string> ve;
for(int i=1;i<(1<<n);i++)
{
dp[i].score=inf;
//因为题目是字典序增的输入,你这样一会输出就是最小字典序了
//一个小技巧吧学到了
for(int j=n-1;j>=0;j--)
{
if(i&(1<<j)){//可能上一个状态
int pre=i-(1<<j);
//被扣的分完成上一个的扣分
int punish=dp[pre].endtime+a[j].c-a[j].d;
if(punish<0) punish=0;
if(dp[i].score>dp[pre].score+punish)
{//更新最小扣分
dp[i].score=dp[pre].score+punish;
dp[i].last=pre;
dp[i].cur=j;
dp[i].endtime=dp[pre].endtime+a[j].c;
}
}
}
}
int final=(1<<n)-1;
printf("%d\n",dp[final].score);
while(final){
int tem=dp[final].cur;
ve.push_back(a[tem].s);
final=dp[final].last;
}
for(int i=ve.size()-1;i>=0;i--)
cout<<ve[i]<<endl;
}
return 0;
}
标签:return 完成 txt 思路 article 字典序 csdn 分析 sdn
原文地址:https://www.cnblogs.com/mch5201314/p/10629039.html