您的位置:首页 > 编程语言 > C语言/C++

CCPC - k题(dp)

2015-10-27 10:54 471 查看
本题目的意思:

给定n个位置,每个位置有一个二元组,要求确定n个位置的属性(选1或者是2),使得每个二元组的1属性和2属性去最近的位置,总代价最小(代价 属性值*移动距离)

分析:

可以尝试如此规划,定义d[ i ][ j ]为当前修到了i位置,i位置的属性为j,那么下面的一段连续路径要修的属性为(3-j),这一段路径上的属性为(3-j)的不需要移动,剩下的要移动到两边。

这个题目难得地方在于怎样设计状态使得具有很好的规划性质。

附: 交题点击打开链接

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep1(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) for(int i=0;i<(int)n;i++)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 4010;
int n;
struct node
{
ll pre
,pp
,suf
,ss
;
void init()
{
pre[0]=pp[0]=suf[n+1]=ss[n+1]=0;
rep1(i,1,n) pre[i]=pre[i-1]+pre[i],pp[i]=pre[i]+pp[i-1];
for(int i=n; i>=1; i--) suf[i]=suf[i+1]+suf[i],ss[i]=ss[i+1]+suf[i];
}
ll get_lr(int i,int j)
{
int mid=(j-i-1)/2+i , llen = mid-i , rlen = j-mid-1;
ll sum = 0;
sum+=ss[i+1]-ss[mid+1]-suf[mid+1]*llen;
sum+=pp[j-1]-pp[mid]-pre[mid]*rlen;
return sum;
}
ll get(int i,int j)
{
if(i == 0) return pp[j-1];
else if(j == n+1) return ss[i+1];
return get_lr(i,j);
}
} T,P;
ll d
[2];
bool vis
[2];
ll dp(int i,int j)
{
if(vis[i][j]) return d[i][j];
vis[i][j] = true;
if(i == n) return d[i][j] = 0;
int lim = (i==0 ? n-1 : n);
rep1(k,i+1,lim)
{
ll add=(j==0 ? T.get(i,k+1):P.get(i,k+1));
if(k == i+1) d[i][j] = dp(k,j^1)+add;
else  d[i][j] = min(d[i][j],dp(k,j^1)+add);
}
return d[i][j];
}
int main()
{
int Ta,kase=1;
scanf("%d",&Ta);
while(Ta--)
{
scanf("%d",&n);
int x,y;
rep1(i,1,n) scanf("%d %d",&x,&y),T.pre[i]=T.suf[i]=x,P.pre[i]=P.suf[i]=y;
T.init();
P.init();
memset(vis,false,sizeof(vis));
cout<<"Case #"<<kase++<<": "<<min(dp(0,0),dp(0,1))<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  algorithm c++ 算法