题目描述 Description
多瑞卡得到了一份有趣而高薪的工作。每天早晨他必须关掉他所在村庄的街灯。所有的街灯都被设置在一条直路的同一侧。
多瑞卡每晚到早晨5点钟都在晚会上,然后他开始关灯。开始时,他站在某一盏路灯的旁边。
每盏灯都有一个给定功率的电灯泡,因为多端卡有着自觉的节能意识,他希望在耗能总数最少的情况下将所有的灯关掉。
多端卡因为太累了,所以只能以1m/s的速度行走。关灯不需要花费额外的时间,因为当他通过时就能将灯关掉。
编写程序,计算在给定路灯设置,灯泡功率以及多端卡的起始位置的情况下关掉所有的灯需耗费的最小能量。
输入文件的第一行包含一个整数N,2≤N≤1000,表示该村庄路灯的数量。
第二行包含一个整数V,1≤V≤N,表示多瑞卡开始关灯的路灯号码。
接下来的N行中,每行包含两个用空格隔开的整数D和W,用来描述每盏灯的参数,其中0≤D≤1000,0≤W≤1000。D表示该路灯与村庄开始处的距离(用米为单位来表示),W表示灯泡的功率,即在每秒种该灯泡所消耗的能量数。路灯是按顺序给定的。
输出文件的第一行即唯一的一行应包含一个整数,即消耗能量之和的最小值。注意结果小超过1,000,000,000。
4
3
2 2
5 8
6 1
8 7
56
思路:鬼畜区间DP,神TM这也可以作为状态!令dp[i][j][0/1]表示关闭区间[i,j]内的灯人最后在左或右侧的最小代价(0为左侧,1为右侧),则递推完后,min(dp[1][n][0],dp[1][n][1])即为最后的答案。
说实话,这是我见过的仅次于打砖块的一个最恶心而鬼畜的DP了,明明看似自己可以做,但就是TM做不出来。。。一开始定义二维数组dp[i][j]表示在i位置已经关了j盏灯的最小代价,可是自己转移了转移发现有后效性。。无奈想,不会是状压吧。。emm怎么可能,2^1000的空间早就炸上天了。。。
原来这题还用到一个贪心思想:从一个位置走到另一个位置,沿途的路灯总是都顺路关能使答案尽量优,在这个贪心的前提下,再进行DP,得到的一定是最优解。
原谅我DP学艺不精,遇到这种题就跪,我实在是太菜了QAQ。
一开始疯狂到打了个纯暴力+树状数组维护的40分T上天的代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> using namespace std; const int maxn=50+5; #define lowbit(x) x&-x; int read() { int ret=0,f=1; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f-=1;c=getchar();} while(c>='0'&&c<='9') {ret=ret*10+c-'0';c=getchar();} return ret*f; } int n,s; int pos[maxn],num[maxn]; int tree[maxn]; void change(int x,int v) { while(x<=n) { tree[x]+=v; x+=lowbit(x); } } int ask(int r) { int ret=0; while(r) { ret+=tree[r]; r-=lowbit(r); } return ret; } int ans=2147483647; bool used[maxn]; void dfs(int now,int sum,int step) { if(step==n) { ans=min(ans,sum); return; } for(int i=1;i<=n;i++) { if(!used[i]) { used[i]=1; sum+=abs(pos[now]-pos[i])*ask(n); change(i,-num[i]); dfs(i,sum,step+1); change(i,num[i]); sum-=abs(pos[now]-pos[i])*ask(n); used[i]=0; } } } int main() { n=read(),s=read(); for(int i=1;i<=n;i++) { pos[i]=read(),num[i]=read(); change(i,num[i]); } used[s]=1; change(s,-num[s]); dfs(s,0,1); printf("%d ",ans); return 0; }
后来才发现的鬼畜区间DP的AC代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> using namespace std; const int maxn=1000+5; typedef long long ll; int read() { int ret=0,f=1; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') {ret=ret*10+c-'0';c=getchar();} return ret*f; } int n,s; int pos[maxn],sum[maxn]; ll dp[maxn][maxn][2]; int main() { memset(dp,0x3f,sizeof(dp)); n=read(),s=read(); int x; for(int i=1;i<=n;i++) { pos[i]=read(),x=read(); sum[i]=sum[i-1]+x; } dp[s][s][0]=dp[s][s][1]=0; for(int j=s;j<=n;j++) for(int i=j-1;i>=1;i--) { dp[i][j][0]=min(dp[i+1][j][0]+(pos[i+1]-pos[i])*(sum[n]-sum[j]+sum[i]), dp[i+1][j][1]+(pos[j]-pos[i])*(sum[n]-sum[j]+sum[i])); dp[i][j][1]=min(dp[i][j-1][1]+(pos[j]-pos[j-1])*(sum[n]-sum[j-1]+sum[i-1]), dp[i][j-1][0]+(pos[j]-pos[i])*(sum[n]-sum[j-1]+sum[i-1])); } printf("%lld ",min(dp[1][n][0],dp[1][n][1])); return 0; }