之前见到这道题时没像出来怎么写,今天在网上搜了一下,发现了一个名词-状态压缩的动态规划。在这个题里的状态压缩的表现形式就是用二进制表示已选集合,这样做就可以用动态规划来解这种解空间为排序树的题了,之前见过几道这种题,都卡在如何表示已选集合上面。收获不小。
#include<stdio.h>
int dp[1 << 16], end[1 << 16], back[1 << 16];
int d[16], c[16];
char name[16][110];
void trackback(int n)//回溯输出解
{
if(back[n] > 0)
{
trackback(back[n]);
}
//在这里坑了一会,没有搞清楚name[n]和name[i]
int i = 0;
int mark = n ^ back[n];
while(mark > 1)
{
i++;
mark >>= 1;
}
printf("%s\n", name[i]);
}
int main(void)
{
int t;
scanf("%d", &t);
while(t-- > 0)
{
int n, len;
int i, j;
scanf("%d", &n);
len = 1 << n;//这是总共的集合数量
for(i = 0; i < n; i++)
{
scanf("%s%d%d", &name[i], &d[i], &c[i]);
}
dp[0] = 0;//初始值为空集
end[0] = 0;//初始结束时间
for(i = 1; i < len; i++)
{
dp[i] = 0x0fffffff;
}
for(i = 0; i < len - 1; i++)
{
for(j = 0; j < n; j++)//j表示当前选中位
{
int mark = 1 << j;//掩码
int nEnd = end[i] + c[j];
int nc = (nEnd > d[j] ? nEnd - d[j] : 0) + dp[i];
if((i & mark) == 0 && nc < dp[i + mark])
{
dp[i + mark] = nc;
end[i + mark] = nEnd;
back[i + mark] = i;
}
}
}
printf("%d\n", dp[len - 1]);
trackback(len - 1);
}
return 0;
}